Oui, j'ai fait cela dans le passé avec les propriétés ActualWidth
and ActualHeight
, qui sont toutes deux en lecture seule. J'ai créé un comportement attaché qui a ObservedWidth
et des ObservedHeight
propriétés attachées. Il a également une Observe
propriété qui est utilisée pour faire le raccordement initial. L'utilisation ressemble à ceci:
<UserControl ...
SizeObserver.Observe="True"
SizeObserver.ObservedWidth="{Binding Width, Mode=OneWayToSource}"
SizeObserver.ObservedHeight="{Binding Height, Mode=OneWayToSource}"
Ainsi , le modèle d'affichage a Width
et Height
propriétés qui sont toujours en phase avec les ObservedWidth
et ObservedHeight
propriétés attachées. La Observe
propriété s'attache simplement à l' SizeChanged
événement du FrameworkElement
. Dans le handle, il met à jour ses propriétés ObservedWidth
et ObservedHeight
. Ergo, le Width
et Height
du modèle de vue est toujours synchronisé avec le ActualWidth
et ActualHeight
du UserControl
.
Peut-être pas la solution parfaite (je suis d'accord - les DP en lecture seule devraient prendre en charge les OneWayToSource
liaisons), mais cela fonctionne et il maintient le modèle MVVM. De toute évidence, les DP ObservedWidth
et neObservedHeight
sont pas en lecture seule.
UPDATE: voici le code qui implémente la fonctionnalité décrite ci-dessus:
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
propriété, combinant hauteur et largeur. Environ. 50% de code en moins.ActualSize
propriétéFrameworkElement
. Si vous souhaitez une liaison directe des propriétés attachées, vous devez créer deux propriétés à lier respectivement àActualWidth
etActualHeight
.J'utilise une solution universelle qui fonctionne non seulement avec ActualWidth et ActualHeight, mais également avec toutes les données auxquelles vous pouvez vous lier au moins en mode lecture.
Le balisage ressemble à ceci, à condition que ViewportWidth et ViewportHeight soient des propriétés du modèle de vue
Voici le code source des éléments personnalisés
la source
Target
propriété doit être rendue inscriptible même si elle ne doit pas être modifiée de l'extérieur: - /Si quelqu'un d'autre est intéressé, j'ai codé une approximation de la solution de Kent ici:
N'hésitez pas à l'utiliser dans vos applications. Ça marche bien. (Merci Kent!)
la source
Voici une autre solution à ce «bug» dont j'ai parlé ici:
OneWayToSource Binding for ReadOnly Dependency Property
Cela fonctionne en utilisant deux propriétés de dépendance, Listener et Mirror. Listener est lié OneWay à TargetProperty et dans PropertyChangedCallback il met à jour la propriété Mirror qui est liée OneWayToSource à ce qui a été spécifié dans la liaison. Je l'appelle
PushBinding
et il peut être défini sur n'importe quelle propriété de dépendance en lecture seule comme celle-ciTéléchargez le projet de démonstration ici .
Il contient le code source et un court exemple d'utilisation, ou visitez mon blog WPF si vous êtes intéressé par les détails de l'implémentation.
Une dernière note, depuis .NET 4.0, nous sommes encore plus éloignés de la prise en charge intégrée de cela, car une liaison OneWayToSource lit la valeur à partir de la source après l'avoir mise à jour
la source
J'aime la solution de Dmitry Tashkinov! Cependant, il a planté mon VS en mode conception. C'est pourquoi j'ai ajouté une ligne à la méthode OnSourceChanged:
la source
Je pense que cela peut être fait un peu plus simple:
xaml:
cs:
la source