2009-03-18 9 views
71

मैं OneWayToSource मोड के रूप में के साथ एक Readonly संपत्ति के लिए बाध्य करने के लिए कोशिश कर रहा हूँ, लेकिन यह इस XAML में नहीं किया जा सकता लगती में केवल पढ़ने के लिए संपत्ति से बाध्यकारी:OneWayToSource XAML

<controls:FlagThingy IsModified="{Binding FlagIsModified, 
              ElementName=container, 
              Mode=OneWayToSource}" /> 

मैं:

संपत्ति 'FlagThingy.IsModified' सेट नहीं की जा सकती क्योंकि इसमें पहुंच योग्य सेट एक्सेसर नहीं है।

IsModifiedFlagThingy पर एक केवल पढ़ने के लिए DependencyProperty है। मैं कंटेनर पर FlagIsModified संपत्ति को उस मान को बांधना चाहता हूं।

स्पष्ट है:

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

सिर्फ XAML का उपयोग करना संभव है?


अपडेट: वैसे, मुझे इस मामले कंटेनर पर बंधन और FlagThingy पर नहीं की स्थापना द्वारा तय की। लेकिन मैं अभी भी जानना चाहूंगा कि यह संभव है या नहीं।

+0

के लिए विशिष्ट है लेकिन आप एक रीड ओनली संपत्ति के मूल्य निर्धारित कर सकते हैं? – idursun

+2

आप नहीं कर सकते। यह भी नहीं है कि मैं क्या हासिल करने की कोशिश कर रहा हूं। मैं संपत्ति 'फ्लैगआईजोडिफाइड' को रीडराइट करने के लिए रीडोनली प्रॉपर्टी 'IsModified' से प्राप्त करने का प्रयास कर रहा हूं। – Inferis

+0

अच्छा सवाल। आपका कामकाज केवल तभी काम करता है जब कंटेनर एक निर्भरता ऑब्जेक्ट है और फ्लैगआईएसमोडिफाइड एक निर्भरता प्रॉपर्टी है। –

उत्तर

40

OneWayToSource के लिए कुछ शोध परिणाम ...

विकल्प # 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); 

विकल्प # 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}" /> 

विकल्प # 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; 
} 
<controls:FlagThingy x:Name="_flagThingy" /> 
// 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"); 
    } 
.... 
+21

तो यह वास्तव में एक बग है। – Inferis

+0

अच्छा शोध। यदि आपने इसे इतनी अच्छी तरह से बाहर नहीं रखा है, तो मैं वही, दर्दनाक पथ चलाता। @ इन्फेरिस से सहमत हैं। – kevinarpe

+0

क्या यह एक बग है? एक OneWayToSource बाइंडिंग को केवल पढ़ने-योग्य निर्भरता प्रॉपर्टी के साथ अनुमति क्यों नहीं दी जाएगी? –

0

डब्ल्यूपीएफ सीएलआर प्रॉपर्टी सेटर का उपयोग नहीं करेगा, लेकिन ऐसा लगता है कि यह इसके आधार पर कुछ अजीब सत्यापन करता है।

अपनी स्थिति में हो सकता है यह ठीक हो सकता है:

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

इस मामले में सीएलआर संपत्ति का उपयोग नहीं किया जाता है। – Inferis

+0

क्या आपका मतलब है कि आपने केवल निर्भरता प्रॉपर्टी को परिभाषित किया है और <नियंत्रण: फ्लैगिंगी IsModified = "..." /> लिखने में सक्षम था? मेरे लिए यह कहता है: "संपत्ति 'IsModified' एक्सएमएल नेमस्पेस में मौजूद नहीं है" अगर मैं सीएलआर संपत्ति नहीं जोड़ता हूं। – alex2k8

+1

मेरा मानना ​​है कि डिज़ाइन का समय क्लियर गुणों का उपयोग करता है, जहां रनटाइम वास्तव में निर्भरता संपत्ति पर सीधे जाता है (यदि यह एक है)। – meandmycode

-1

आप गलत दिशा में बाध्यकारी अभी कर रहे हैं। जब भी आपके द्वारा बनाए जा रहे नियंत्रण पर IsModified परिवर्तन होते हैं, तो OneWayToSource कंटेनर पर फ़्लैगआईमोडिफाइड को आजमाएगा और अपडेट करेगा। आप विपरीत चाहते हैं, जो IsModified कंटेनर से बांधना है। FlagIsModified। उसके लिए आपको बाध्यकारी मोड Oneway

<controls:FlagThingy IsModified="{Binding FlagIsModified, 
              ElementName=container, 
              Mode=OneWay}" /> 

गणन सदस्यों की पूर्ण सूची का उपयोग करना चाहिए: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx

+3

नहीं, मैं बिल्कुल परिदृश्य चाहता हूं जिसे आप वर्णन करते हैं कि मैं नहीं करना चाहता हूं। फ्लैगिंगिंग। आईएसमोडाइफाइड -> कंटेनर। फ्लैगआईएस संशोधित – Inferis

+3

नीचे चिह्नित होना क्योंकि प्रश्नकर्ता के पास एक अस्पष्ट सवाल थोड़ा अधिक लगता है। – JaredPar

+4

@ जेरेडपार: मुझे नहीं पता कि सवाल के बारे में संदिग्ध क्या है। सवाल बताता है कि 1) केवल पढ़ने-योग्य निर्भरता संपत्ति 'आईएसआईमोडिफाइड' है, 2) ओपी एक्सएएमएल में उस संपत्ति पर बाध्यकारी घोषित करना चाहता है और वह 3) बाध्यकारी को 'वनवेटसोर्स' मोड में काम करना चाहिए। आपका समाधान व्यावहारिक रूप से काम नहीं करता है, जैसा कि प्रश्न में वर्णित है, संकलक आपको केवल पढ़ने योग्य संपत्ति पर बाध्यकारी घोषित नहीं करेगा, और यह अवधारणात्मक रूप से काम नहीं करता है क्योंकि 'IsModified' केवल पढ़ने के लिए है और इस प्रकार मूल्य बदला नहीं जा सकता (बाध्यकारी द्वारा)। –

18

इस WPF की एक सीमा है और यह डिजाइन कर रहा है। यह कनेक्ट यहाँ पर सूचना दी है:
OneWayToSource binding from a readonly dependency property

मैं एक समाधान बनाया गतिशील स्रोत PushBinding जो मैं blogged about here कहा जाता है के लिए केवल पढ़ने के लिए निर्भरता गुण पुश करने के लिए सक्षम होने के लिए। उदाहरण के नीचे दो निर्भरता गुण, श्रोता और दर्पण का उपयोग करके रीड-ओनली डी पी के ActualWidth और ActualHeight चौड़ाई के लिए और DataContext

<TextBlock Name="myTextBlock"> 
    <pb:PushBindingManager.PushBindings> 
     <pb:PushBinding TargetProperty="ActualHeight" Path="Height"/> 
     <pb:PushBinding TargetProperty="ActualWidth" Path="Width"/> 
    </pb:PushBindingManager.PushBindings> 
</TextBlock> 

PushBinding कार्यों की ऊंचाई गुणों से OneWayToSource बाइंडिंग करता है। श्रोता OneWay को लक्ष्यप्रोपर्टी में बाध्य किया गया है और PropertyChangedCallback में यह मिरर प्रॉपर्टी को अपडेट करता है जो OneWayToSource बाध्यकारी में निर्दिष्ट किए गए अनुसार है।

Demo Project can be Downloaded Here.
यह स्रोत कोड और छोटा नमूना उपयोग जाएँ my WPF blog यदि आप कार्यान्वयन विवरण में रुचि रखते हैं हैं, या।

+0

दिलचस्प! मैं एक समान समाधान के साथ आया और इसे "कंडिट" कहा जाता है - कंडिट के आपके डिजाइन और दो अलग-अलग बाइंडिंग के अनुसार दो निर्भरता गुण थे। एक्सएएमएल में सादे पुराने गुणों के लिए मेरे पास उपयोग का मामला सादे पुराने गुणों को बाध्य कर रहा था। –

+2

मुझे लगता है कि आपका एमएस कनेक्ट लिंक अब और काम नहीं करता है। क्या इसका मतलब है कि एमएस ने इसे .NET के नए संस्करण में तय किया है या उन्होंने इसे अभी हटा दिया है? – Tiny

+0

2015-06-04 को हल नहीं किया गया प्रतीत होता है। .NET 4.5 –

0

हमम ... मुझे यकीन नहीं है कि मैं इनमें से किसी भी समाधान से सहमत हूं। बाहरी संपत्ति को अनदेखा करते हुए आपके संपत्ति पंजीकरण में एक जबरन कॉलबैक निर्दिष्ट करने के बारे में कैसे? उदाहरण के लिए, मुझे उपयोगकर्ता नियंत्रण के अंदर मीडियाइलेमेंट नियंत्रण की स्थिति प्राप्त करने के लिए केवल पढ़ने-योग्य स्थिति निर्भरता प्रॉपर्टी को लागू करने की आवश्यकता है। यहाँ कैसे मैंने किया है:

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 नियंत्रण है।

+0

बहुत बुरा यह बीसीएल कक्षाओं के पूर्व परिभाषित गुणों के लिए काम नहीं करता है: - / –

5

यह लिखा:

उपयोग:

<TextBox Text="{Binding Text}" 
     p:OneWayToSource.Bind="{p:Paths From={x:Static Validation.HasErrorProperty}, 
             To=SomeDataContextProperty}" /> 

कोड:

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

अभी तक शैलियों और टेम्पलेट्स में यह परीक्षण नहीं किया है, लगता है कि यह विशेष आवरण की जरूरत है।

1

यहाँ एक और संलग्न संपत्ति SizeObserver के आधार पर समाधान है नियंत्रण में यहाँ Pushing read-only GUI properties back into 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); 
    } 
} 

प्रचार जुड़ी संपत्ति विस्तृत

<ListView ItemsSource="{Binding SomeGridItems}"        
    ut:MouseObserver.Observe="True" 
    ut:MouseObserver.ObservedMouseOver="{Binding IsMouseOverGrid, Mode=OneWayToSource}">  
1

यहाँ मान्यकरण के लिए बाध्य के लिए एक और कार्यान्वयन है।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 में प्रयोग

<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> 

इस कार्यान्वयन बाध्यकारी Validation.HasError