Привязка OneWayToSource от свойства readonly в XAML

Я пытаюсь связать свойство Readonly с OneWayToSource как режим, но, похоже, это невозможно сделать в XAML:

  

Я получил:

Свойство «FlagThingy.IsModified» не может быть установлено, потому что у него нет доступного доступа для доступа.

IsModified является readonly DependencyProperty на FlagThingy . Я хочу привязать это значение к свойству FlagIsModified в контейнере.

Чтобы быть ясным:

 FlagThingy.IsModified --> container.FlagIsModified ------ READONLY ----- ----- READWRITE -------- 

Можно ли использовать только XAML?


Обновление: Ну, я исправил этот случай, установив привязку в контейнере, а не на FlagThingy . Но я все равно хотел бы знать, возможно ли это.

Некоторые результаты исследований для OneWayToSource …

Опция 1.

 // Control definition public partial class FlagThingy : UserControl { public static readonly DependencyProperty IsModifiedProperty = DependencyProperty.Register("IsModified", typeof(bool), typeof(FlagThingy), new PropertyMetadata()); } 
  
 // Binding Code Binding binding = new Binding(); binding.Path = new PropertyPath("FlagIsModified"); binding.ElementName = "container"; binding.Mode = BindingMode.OneWayToSource; _flagThingy.SetBinding(FlagThingy.IsModifiedProperty, binding); 

Вариант №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"); } } } 
  

Вариант № 3 (Истинное свойство зависимостей только для чтения)

System.ArgumentException: свойство IsModified не может быть привязано к данным.

 // 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; } 
  
 // Binding Code Same binding code... 

Отражатель дает ответ:

 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"); } .... 

Это ограничение WPF, и оно по дизайну. Сообщается о Connect здесь:
Связывание OneWayToSource из свойства readonly dependency

Я сделал решение для динамического использования свойств зависимостей только для чтения с источником, называемым PushBinding котором я писал здесь . В приведенном ниже OneWayToSource привязки ActualWidth ActualHeight к значениям ActualWidth и ActualHeight для ActualHeight в свойствах ширины и высоты DataContext

       

PushBinding работает с использованием двух свойств зависимостей, прослушивателя и зеркала. Listener привязано OneWay к TargetProperty и в PropertyChangedCallback обновляет свойство Mirror, которое привязано OneWayToSource к тому, что было указано в Binding.

Демо-проект можно скачать здесь.
Он содержит исходный код и краткое использование примера, или посетите мой блог WPF, если вас интересуют детали реализации.

Написал это:

Применение:

  

Код:

 using System; using System.Windows; using System.Windows.Data; using System.Windows.Markup; public static class OneWayToSource { public static readonly DependencyProperty BindProperty = DependencyProperty.RegisterAttached( "Bind", typeof(ProxyBinding), typeof(OneWayToSource), new PropertyMetadata(default(Paths), OnBindChanged)); public static void SetBind(this UIElement element, ProxyBinding value) { element.SetValue(BindProperty, value); } [AttachedPropertyBrowsableForChildren(IncludeDescendants = false)] [AttachedPropertyBrowsableForType(typeof(UIElement))] public static ProxyBinding GetBind(this UIElement element) { return (ProxyBinding)element.GetValue(BindProperty); } private static void OnBindChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((ProxyBinding)e.OldValue)?.Dispose(); } public class ProxyBinding : DependencyObject, IDisposable { private static readonly DependencyProperty SourceProxyProperty = DependencyProperty.Register( "SourceProxy", typeof(object), typeof(ProxyBinding), new PropertyMetadata(default(object), OnSourceProxyChanged)); private static readonly DependencyProperty TargetProxyProperty = DependencyProperty.Register( "TargetProxy", typeof(object), typeof(ProxyBinding), new PropertyMetadata(default(object))); public ProxyBinding(DependencyObject source, DependencyProperty sourceProperty, string targetProperty) { var sourceBinding = new Binding { Path = new PropertyPath(sourceProperty), Source = source, Mode = BindingMode.OneWay, }; BindingOperations.SetBinding(this, SourceProxyProperty, sourceBinding); var targetBinding = new Binding() { Path = new PropertyPath($"{nameof(FrameworkElement.DataContext)}.{targetProperty}"), Mode = BindingMode.OneWayToSource, Source = source }; BindingOperations.SetBinding(this, TargetProxyProperty, targetBinding); } public void Dispose() { BindingOperations.ClearAllBindings(this); } private static void OnSourceProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.SetCurrentValue(TargetProxyProperty, e.NewValue); } } } [MarkupExtensionReturnType(typeof(OneWayToSource.ProxyBinding))] public class Paths : MarkupExtension { public DependencyProperty From { get; set; } public string To { get; set; } public override object ProvideValue(IServiceProvider serviceProvider) { var provideValueTarget = (IProvideValueTarget)serviceProvider.GetService(typeof(IProvideValueTarget)); var targetObject = (UIElement)provideValueTarget.TargetObject; return new OneWayToSource.ProxyBinding(targetObject, this.From, this.To); } } 

Не проверял его в стилях и шаблонах, предположим, что для этого нужен специальный корпус.

Вот еще одна реализация для привязки к Validation.HasError

 public static class OneWayToSource { public static readonly DependencyProperty BindingsProperty = DependencyProperty.RegisterAttached( "Bindings", typeof(OneWayToSourceBindings), typeof(OneWayToSource), new PropertyMetadata(default(OneWayToSourceBindings), OnBinidngsChanged)); public static void SetBindings(this FrameworkElement element, OneWayToSourceBindings value) { element.SetValue(BindingsProperty, value); } [AttachedPropertyBrowsableForChildren(IncludeDescendants = false)] [AttachedPropertyBrowsableForType(typeof(FrameworkElement))] public static OneWayToSourceBindings GetBindings(this FrameworkElement element) { return (OneWayToSourceBindings)element.GetValue(BindingsProperty); } private static void OnBinidngsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { ((OneWayToSourceBindings)e.OldValue)?.ClearValue(OneWayToSourceBindings.ElementProperty); ((OneWayToSourceBindings)e.NewValue)?.SetValue(OneWayToSourceBindings.ElementProperty, d); } } public class OneWayToSourceBindings : FrameworkElement { private static readonly PropertyPath DataContextPath = new PropertyPath(nameof(DataContext)); private static readonly PropertyPath HasErrorPath = new PropertyPath($"({typeof(Validation).Name}.{Validation.HasErrorProperty.Name})"); public static readonly DependencyProperty HasErrorProperty = DependencyProperty.Register( nameof(HasError), typeof(bool), typeof(OneWayToSourceBindings), new FrameworkPropertyMetadata(default(bool), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); internal static readonly DependencyProperty ElementProperty = DependencyProperty.Register( "Element", typeof(UIElement), typeof(OneWayToSourceBindings), new PropertyMetadata(default(UIElement), OnElementChanged)); private static readonly DependencyProperty HasErrorProxyProperty = DependencyProperty.RegisterAttached( "HasErrorProxy", typeof(bool), typeof(OneWayToSourceBindings), new PropertyMetadata(default(bool), OnHasErrorProxyChanged)); public bool HasError { get { return (bool)this.GetValue(HasErrorProperty); } set { this.SetValue(HasErrorProperty, value); } } private static void OnHasErrorProxyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { d.SetCurrentValue(HasErrorProperty, e.NewValue); } private static void OnElementChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { if (e.NewValue == null) { BindingOperations.ClearBinding(d, DataContextProperty); BindingOperations.ClearBinding(d, HasErrorProxyProperty); } else { var dataContextBinding = new Binding { Path = DataContextPath, Mode = BindingMode.OneWay, Source = e.NewValue }; BindingOperations.SetBinding(d, DataContextProperty, dataContextBinding); var hasErrorBinding = new Binding { Path = HasErrorPath, Mode = BindingMode.OneWay, Source = e.NewValue }; BindingOperations.SetBinding(d, HasErrorProxyProperty, hasErrorBinding); } } } 

Использование в xaml

         

Эта реализация специфична для привязки Validation.HasError

Вот еще одно прилагаемое решение на основе подробного описания SizeObserver. Нажатие свойств GUI только для чтения обратно в 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); } } 

Объявлять прикрепленное свойство под контролем

  

WPF не будет использовать средство определения свойств CLR, но, похоже, на нем выполняется нечетная валидация.

Может быть, в вашей ситуации это может быть нормально:

  public bool IsModified { get { return (bool)GetValue(IsModifiedProperty); } set { throw new Exception("An attempt ot modify Read-Only property"); } } 

Хм … Я не уверен, что согласен с любым из этих решений. Как насчет указания обратного вызова принуждения в вашей регистрации собственности, который игнорирует внешние изменения? Например, мне нужно было реализовать свойство зависимостей позиции только для чтения, чтобы получить позицию элемента управления MediaElement внутри пользовательского элемента управления. Вот как я это сделал:

  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); } } 

Другими словами, просто игнорируйте изменение и возвращайте значение, поддерживаемое другим членом, у которого нет общедоступного модификатора. – В приведенном выше примере MediaRenderer фактически является частным элементом управления MediaElement.

Вы делаете привязку в неправильном направлении прямо сейчас. OneWayToSource будет пытаться обновлять FlagIsModified на контейнере всякий раз, когда IsModified изменяет элемент управления, который вы создаете. Вы хотите противоположное, которое должно иметь привязку IsModified к контейнеру. FlagIsModified. Для этого вы должны использовать режим привязки OneWay

  

Полный список участников enums: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx

  • Должен ли я привязываться к ICollectionView или ObservableCollection
  • WPF привязка данных к интерфейсу, а не к фактическому объекту - возможность литья?
  • Что означает «{Binding Path =.}» В привязке WPF?
  • Как я могу привязать данные строк к ListBox в WPF / WP7?
  • WPF MVVM INotifyPropertyChanged Implementation - модель или ViewModel
  • Как правильно привязать xml к WPF DataGrid?
  • Похоже, что привязки данных не обновляются
  • Обнаружение ошибок проверки WPF
  • Связывающие свойства в коде
  • Привязка данных Android с использованием тега include
  • Каковы различные режимы привязки WPF?
  • Interesting Posts

    Как получить и установить переменные среды в C #?

    Как открыть emacs от Finder Mac?

    Android, как запуститьOnUiThread в другом classе?

    Каков самый быстрый способ выбора элементов-потомков в jQuery?

    Как преобразовать подкаст в аудиокнигу / музыкальный трек в iTunes?

    Недопустимый токен aspnet для идентификации

    Почему в этом коде openmp происходит сбой сегментации?

    Предоставляет ли компилятор возможность оптимизировать распределение памяти кучи?

    Изменение формы одной точки в JFreeChart XYPLot

    Можем ли мы заменить память и жесткий диск для ноутбуков – Compaq Presario v2000?

    Почему мой компьютер переходит «Новое USB-устройство» каждый раз, когда я подключаю его к другому порту?

    В чем разница между «локальными» и «роуминговыми» папками?

    Может ли FFmpeg использоваться в качестве библиотеки вместо автономной программы?

    В чем разница между i ++ и ++ i?

    Websocket vs REST при отправке данных на сервер

    Давайте будем гением компьютера.