J'essaie de me lier à une Readonly
propriété avec OneWayToSource
le mode as, mais il semble que cela ne puisse pas être fait en XAML:
<controls:FlagThingy IsModified="{Binding FlagIsModified,
ElementName=container,
Mode=OneWayToSource}" />
Je reçois:
La propriété 'FlagThingy.IsModified' ne peut pas être définie car elle ne dispose pas d'un accesseur d'ensemble accessible.
IsModified
est une lecture seule DependencyProperty
sur FlagThingy
. Je veux lier cette valeur à la FlagIsModified
propriété sur le conteneur.
Pour être clair:
FlagThingy.IsModified --> container.FlagIsModified
------ READONLY ----- ----- READWRITE --------
Est-ce possible en utilisant uniquement XAML?
Mise à jour: Eh bien, j'ai résolu ce cas en définissant la liaison sur le conteneur et non sur le FlagThingy
. Mais j'aimerais quand même savoir si c'est possible.
wpf
data-binding
xaml
readonly
Inferis
la source
la source
IsModified
à la propriété readwriteFlagIsModified
.Réponses:
Quelques résultats de recherche pour OneWayToSource ...
Option 1.
// Control definition public partial class FlagThingy : UserControl { public static readonly DependencyProperty IsModifiedProperty = DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata()); }
<controls:FlagThingy x:Name="_flagThingy" />
// Binding Code Binding binding = new Binding(); binding.Path = new PropertyPath("FlagIsModified"); binding.ElementName = "container"; binding.Mode = BindingMode.OneWayToSource; _flagThingy.SetBinding(FlagThingy.IsModifiedProperty, binding);
Option 2
// Control definition public partial class FlagThingy : UserControl { public static readonly DependencyProperty IsModifiedProperty = DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata()); public bool IsModified { get { return (bool)GetValue(IsModifiedProperty); } set { throw new Exception("An attempt ot modify Read-Only property"); } } }
<controls:FlagThingy IsModified="{Binding Path=FlagIsModified, ElementName=container, Mode=OneWayToSource}" />
Option 3 (vraie propriété de dépendance en lecture seule)
System.ArgumentException: la propriété 'IsModified' ne peut pas être liée aux données.
// Control definition public partial class FlagThingy : UserControl { private static readonly DependencyPropertyKey IsModifiedKey = DependencyProperty.RegisterReadOnly("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata()); public static readonly DependencyProperty IsModifiedProperty = IsModifiedKey.DependencyProperty; }
<controls:FlagThingy x:Name="_flagThingy" />
// Binding Code Same binding code...
Reflector donne la réponse:
internal static BindingExpression CreateBindingExpression(DependencyObject d, DependencyProperty dp, Binding binding, BindingExpressionBase parent) { FrameworkPropertyMetadata fwMetaData = dp.GetMetadata(d.DependencyObjectType) as FrameworkPropertyMetadata; if (((fwMetaData != null) && !fwMetaData.IsDataBindingAllowed) || dp.ReadOnly) { throw new ArgumentException(System.Windows.SR.Get(System.Windows.SRID.PropertyNotBindable, new object[] { dp.Name }), "dp"); } ....
la source
DependencyProperty
DP). Un DP en lecture seule ne peut être modifié qu'en utilisant leDependencyPropertyKey
. Pour enregistrer un,BindingExpression
le moteur doit manipuler les métadonnées du DP cible. Comme ilDependencyPropertyKey
est considéré comme privé pour garantir la protection publique en écriture, le moteur devra ignorer cette clé avec pour résultat de ne pas pouvoir enregistrer la liaison sur un DP en lecture seule.Il s'agit d'une limitation de WPF et c'est par conception. Il est signalé sur Connect ici:
liaison OneWayToSource à partir d'une propriété de dépendance en lecture seule
J'ai fait une solution pour pouvoir pousser dynamiquement les propriétés de dépendance en lecture seule vers la source appelée sur
PushBinding
laquelle j'ai blogué ici . L'exemple ci-dessous fait desOneWayToSource
liaisons à partir des DP en lecture seuleActualWidth
etActualHeight
aux propriétés Largeur et Hauteur duDataContext
<TextBlock Name="myTextBlock"> <pb:PushBindingManager.PushBindings> <pb:PushBinding TargetProperty="ActualHeight" Path="Height"/> <pb:PushBinding TargetProperty="ActualWidth" Path="Width"/> </pb:PushBindingManager.PushBindings> </TextBlock>
PushBinding
fonctionne en utilisant deux propriétés de dépendance, écouteur et miroir. Listener est liéOneWay
à TargetProperty et dans lePropertyChangedCallback
il met à jour la propriété Mirror qui est liéeOneWayToSource
à tout ce qui a été spécifié dans la liaison.Le projet de démonstration peut être téléchargé ici.
Il contient le code source et un court exemple d'utilisation.
la source
A écrit ceci:
Usage:
<TextBox Text="{Binding Text}" p:OneWayToSource.Bind="{p:Paths From={x:Static Validation.HasErrorProperty}, To=SomeDataContextProperty}" />
Code:
Je ne l'ai pas encore testé dans les styles et les modèles, je suppose qu'il a besoin d'un boîtier spécial.
la source
Voici une autre solution de propriété attachée basée sur SizeObserver détaillée ici Repousser les propriétés d'interface graphique en lecture seule dans ViewModel
public static class MouseObserver { public static readonly DependencyProperty ObserveProperty = DependencyProperty.RegisterAttached( "Observe", typeof(bool), typeof(MouseObserver), new FrameworkPropertyMetadata(OnObserveChanged)); public static readonly DependencyProperty ObservedMouseOverProperty = DependencyProperty.RegisterAttached( "ObservedMouseOver", typeof(bool), typeof(MouseObserver)); public static bool GetObserve(FrameworkElement frameworkElement) { return (bool)frameworkElement.GetValue(ObserveProperty); } public static void SetObserve(FrameworkElement frameworkElement, bool observe) { frameworkElement.SetValue(ObserveProperty, observe); } public static bool GetObservedMouseOver(FrameworkElement frameworkElement) { return (bool)frameworkElement.GetValue(ObservedMouseOverProperty); } public static void SetObservedMouseOver(FrameworkElement frameworkElement, bool observedMouseOver) { frameworkElement.SetValue(ObservedMouseOverProperty, observedMouseOver); } private static void OnObserveChanged(DependencyObject dependencyObject, DependencyPropertyChangedEventArgs e) { var frameworkElement = (FrameworkElement)dependencyObject; if ((bool)e.NewValue) { frameworkElement.MouseEnter += OnFrameworkElementMouseOverChanged; frameworkElement.MouseLeave += OnFrameworkElementMouseOverChanged; UpdateObservedMouseOverForFrameworkElement(frameworkElement); } else { frameworkElement.MouseEnter -= OnFrameworkElementMouseOverChanged; frameworkElement.MouseLeave -= OnFrameworkElementMouseOverChanged; } } private static void OnFrameworkElementMouseOverChanged(object sender, MouseEventArgs e) { UpdateObservedMouseOverForFrameworkElement((FrameworkElement)sender); } private static void UpdateObservedMouseOverForFrameworkElement(FrameworkElement frameworkElement) { frameworkElement.SetCurrentValue(ObservedMouseOverProperty, frameworkElement.IsMouseOver); } }
Déclarer la propriété jointe dans le contrôle
<ListView ItemsSource="{Binding SomeGridItems}" ut:MouseObserver.Observe="True" ut:MouseObserver.ObservedMouseOver="{Binding IsMouseOverGrid, Mode=OneWayToSource}">
la source
Voici une autre implémentation pour la liaison à Validation.HasError
Utilisation en xaml
<StackPanel> <TextBox Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"> <local:OneWayToSource.Bindings> <local:OneWayToSourceBindings HasError="{Binding HasError}" /> </local:OneWayToSource.Bindings> </TextBox> <CheckBox IsChecked="{Binding HasError, Mode=OneWay}" /> </StackPanel>
Cette implémentation est spécifique à la liaison
Validation.HasError
la source
WPF n'utilisera pas le setter de propriétés CLR, mais il semble qu'il effectue une validation étrange en fonction de celui-ci.
Peut-être dans votre situation cela peut être correct:
la source
Hmmm ... Je ne suis pas sûr d'être d'accord avec l'une de ces solutions. Que diriez-vous de spécifier un rappel de coercition dans votre enregistrement de propriété qui ignore les changements externes? Par exemple, j'avais besoin d'implémenter une propriété de dépendance Position en lecture seule pour obtenir la position d'un contrôle MediaElement dans un contrôle utilisateur. Voici comment je l'ai fait:
public static readonly DependencyProperty PositionProperty = DependencyProperty.Register("Position", typeof(double), typeof(MediaViewer), new FrameworkPropertyMetadata(0d, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault | FrameworkPropertyMetadataOptions.Journal, OnPositionChanged, OnPositionCoerce)); private static void OnPositionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = d as MediaViewer; } private static object OnPositionCoerce(DependencyObject d, object value) { var ctrl = d as MediaViewer; var position = ctrl.MediaRenderer.Position.TotalSeconds; if (ctrl.MediaRenderer.NaturalDuration.HasTimeSpan == false) return 0d; else return Math.Min(position, ctrl.Duration); } public double Position { get { return (double)GetValue(PositionProperty); } set { SetValue(PositionProperty, value); } }
En d'autres termes, ignorez simplement la modification et renvoyez la valeur soutenue par un membre différent qui n'a pas de modificateur public. - Dans l'exemple ci-dessus, MediaRenderer est en fait le contrôle MediaElement privé.
la source
La façon dont j'ai contourné cette limitation était d'exposer uniquement une propriété Binding dans ma classe, en gardant le DependencyProperty privé complètement. J'ai implémenté une propriété en écriture seule "PropertyBindingToSource" (celle-ci n'est pas une DependencyProperty) qui peut être définie sur une valeur de liaison dans le xaml. Dans le setter pour cette propriété en écriture seule, j'appelle à BindingOperations.SetBinding pour lier la liaison à DependencyProperty.
Pour l'exemple spécifique de l'OP, cela ressemblerait à ceci:
L'implémentation FlatThingy:
Notez que l'objet DependencyProperty statique en lecture seule est privé. Dans le contrôle j'ai ajouté un bouton dont le clic est géré par Button_Click. L'utilisation du contrôle FlatThingy dans mon window.xaml:
<Window x:Class="ReadOnlyBinding.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:ReadOnlyBinding" mc:Ignorable="d" DataContext="{x:Static local:ViewModel.Instance}" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <TextBlock Text="{Binding FlagIsModified}" Grid.Row="0" /> <local:FlatThingy IsModifiedBindingToSource="{Binding FlagIsModified, Mode=OneWayToSource}" Grid.Row="1" /> </Grid>
Notez que j'ai également implémenté un ViewModel pour la liaison à qui n'est pas montré ici. Il expose un DependencyProperty nommé "FlagIsModified" comme vous pouvez le glaner à partir de la source ci-dessus.
Cela fonctionne très bien, me permettant de repousser les informations dans le ViewModel à partir de la vue d'une manière faiblement couplée, avec la direction de ce flux d'informations explicitement définie.
la source
Vous faites la reliure dans la mauvaise direction en ce moment. OneWayToSource essaiera de mettre à jour FlagIsModified sur le conteneur chaque fois que IsModified change sur le contrôle que vous créez. Vous voulez le contraire, à savoir que IsModified se lie à container.FlagIsModified. Pour cela, vous devez utiliser le mode de liaison OneWay
<controls:FlagThingy IsModified="{Binding FlagIsModified, ElementName=container, Mode=OneWay}" />
Liste complète des membres de l'énumération: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx
la source
IsIsModified
, que 2) l'OP souhaite déclarer une liaison sur cette propriété en XAML et que 3) la liaison est censée fonctionner enOneWayToSource
mode. Votre solution ne fonctionne pratiquement pas car, comme décrit dans la question, le compilateur ne vous laisse pas déclarer de liaison sur une propriété en lecture seule, et cela ne fonctionne pas conceptuellement car ilIsModified
est en lecture seule et donc sa valeur ne peut pas être changé (par la liaison).