Sim, eu fiz isso no passado com as propriedades ActualWidth
e ActualHeight
, as quais são somente leitura. Eu criei um comportamento anexado que possui ObservedWidth
e ObservedHeight
anexou propriedades. Ele também possui uma Observe
propriedade usada para fazer a conexão inicial. O uso fica assim:
<UserControl ...
SizeObserver.Observe="True"
SizeObserver.ObservedWidth="{Binding Width, Mode=OneWayToSource}"
SizeObserver.ObservedHeight="{Binding Height, Mode=OneWayToSource}"
Assim, o modelo de exibição tem Width
e Height
propriedades que estão sempre em sincronia com os ObservedWidth
e ObservedHeight
anexadas propriedades. A Observe
propriedade simplesmente se anexa ao SizeChanged
evento do FrameworkElement
. No identificador, ele atualiza suas propriedades ObservedWidth
e ObservedHeight
. Portanto, o modelo do Width
e Height
da vista está sempre sincronizado com o ActualWidth
e ActualHeight
do UserControl
.
Talvez não seja a solução perfeita (eu concordo - os DPs somente leitura devem suportar OneWayToSource
ligações), mas funciona e mantém o padrão MVVM. Obviamente, os PDs ObservedWidth
e nãoObservedHeight
são leitura.
UPDATE: aqui está o código que implementa a funcionalidade descrita acima:
public static class SizeObserver
{
public static readonly DependencyProperty ObserveProperty = DependencyProperty.RegisterAttached(
"Observe",
typeof(bool),
typeof(SizeObserver),
new FrameworkPropertyMetadata(OnObserveChanged));
public static readonly DependencyProperty ObservedWidthProperty = DependencyProperty.RegisterAttached(
"ObservedWidth",
typeof(double),
typeof(SizeObserver));
public static readonly DependencyProperty ObservedHeightProperty = DependencyProperty.RegisterAttached(
"ObservedHeight",
typeof(double),
typeof(SizeObserver));
public static bool GetObserve(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (bool)frameworkElement.GetValue(ObserveProperty);
}
public static void SetObserve(FrameworkElement frameworkElement, bool observe)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObserveProperty, observe);
}
public static double GetObservedWidth(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (double)frameworkElement.GetValue(ObservedWidthProperty);
}
public static void SetObservedWidth(FrameworkElement frameworkElement, double observedWidth)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObservedWidthProperty, observedWidth);
}
public static double GetObservedHeight(FrameworkElement frameworkElement)
{
frameworkElement.AssertNotNull("frameworkElement");
return (double)frameworkElement.GetValue(ObservedHeightProperty);
}
public static void SetObservedHeight(FrameworkElement frameworkElement, double observedHeight)
{
frameworkElement.AssertNotNull("frameworkElement");
frameworkElement.SetValue(ObservedHeightProperty, observedHeight);
}
private static void OnObserveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e)
{
var frameworkElement = (FrameworkElement)dependencyObject;
if ((bool)e.NewValue)
{
frameworkElement.SizeChanged += OnFrameworkElementSizeChanged;
UpdateObservedSizesForFrameworkElement(frameworkElement);
}
else
{
frameworkElement.SizeChanged -= OnFrameworkElementSizeChanged;
}
}
private static void OnFrameworkElementSizeChanged(object sender, SizeChangedEventArgs e)
{
UpdateObservedSizesForFrameworkElement((FrameworkElement)sender);
}
private static void UpdateObservedSizesForFrameworkElement(FrameworkElement frameworkElement)
{
// WPF 4.0 onwards
frameworkElement.SetCurrentValue(ObservedWidthProperty, frameworkElement.ActualWidth);
frameworkElement.SetCurrentValue(ObservedHeightProperty, frameworkElement.ActualHeight);
// WPF 3.5 and prior
////SetObservedWidth(frameworkElement, frameworkElement.ActualWidth);
////SetObservedHeight(frameworkElement, frameworkElement.ActualHeight);
}
}
Size
propriedade, combinando altura e largura. Aproximadamente. 50% menos código.ActualSize
propriedadesFrameworkElement
. Se você desejar a ligação direta das propriedades anexadas, deverá criar duas propriedades a serem vinculadasActualWidth
eActualHeight
respectivamente.Eu uso uma solução universal que funciona não apenas com ActualWidth e ActualHeight, mas também com qualquer dado que você possa vincular pelo menos no modo de leitura.
A marcação é assim, desde que ViewportWidth e ViewportHeight sejam propriedades do modelo de visualização
Aqui está o código-fonte para os elementos personalizados
fonte
Target
propriedade precisa ser gravada, mesmo que não deva ser alterada do lado de fora: - /Se alguém mais estiver interessado, codifiquei aqui uma aproximação da solução de Kent:
Sinta-se livre para usá-lo em seus aplicativos. Isso funciona bem. (Obrigado Kent!)
fonte
Aqui está outra solução para esse "bug" sobre o qual escrevi aqui:
Ligação OneWayToSource para propriedade de dependência ReadOnly
Ele funciona usando duas Propriedades de Dependência, Listener e Mirror. O ouvinte está vinculado OneWay ao TargetProperty e no PropertyChangedCallback atualiza a propriedade Mirror, que está vinculada OneWayToSource ao que foi especificado na ligação. Eu chamo
PushBinding
e ele pode ser definido em qualquer propriedade de dependência somente leitura como estaBaixe o Projeto Demo Aqui .
Ele contém código fonte e uso de amostra curta, ou visite meu blog do WPF se você estiver interessado nos detalhes da implementação.
Uma última observação: desde o .NET 4.0, estamos ainda mais distantes do suporte interno para isso, uma vez que a Ligação OneWayToSource lê o valor de volta do Source após a atualização.
fonte
Eu gosto da solução de Dmitry Tashkinov! No entanto, ele travou meu VS no modo de design. É por isso que adicionei uma linha ao método OnSourceChanged:
fonte
Eu acho que isso pode ser feito um pouco mais simples:
xaml:
cs:
fonte