การรวม OneWayToSource จากคุณสมบัติแบบอ่านอย่างเดียวใน XAML


88

ฉันพยายามเชื่อมโยงกับReadonlyคุณสมบัติด้วยOneWayToSourceas mode แต่ดูเหมือนว่าจะไม่สามารถทำได้ใน XAML:

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

ฉันเข้าใจ:

ไม่สามารถตั้งค่าคุณสมบัติ 'FlagThingy.IsModified' เนื่องจากไม่มีตัวเข้าถึงชุดที่สามารถเข้าถึงได้

IsModifiedคืออ่านได้อย่างเดียวบนDependencyProperty FlagThingyฉันต้องการผูกค่านั้นกับFlagIsModifiedคุณสมบัติบนคอนเทนเนอร์

ต้องมีความชัดเจน:

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

เป็นไปได้ไหมโดยใช้ XAML


อัปเดต:ฉันแก้ไขกรณีนี้โดยตั้งค่าการผูกบนคอนเทนเนอร์ไม่ใช่ในไฟล์FlagThingy. แต่ฉันยังอยากรู้ว่าเป็นไปได้ไหม


แต่คุณจะตั้งค่าเป็นคุณสมบัติอ่านอย่างเดียวได้อย่างไร?
— idursun

3
คุณทำไม่ได้ มันไม่ใช่สิ่งที่ฉันพยายามจะบรรลุ ฉันพยายามที่จะได้รับจากการอ่านได้อย่างเดียวคุณสมบัติคุณสมบัติที่อ่านและเขียนIsModified FlagIsModified
— Inferis

คำถามที่ดี. วิธีแก้ปัญหาของคุณใช้ได้เฉพาะเมื่อคอนเทนเนอร์เป็น DependencyObject และ FlagIsModified เป็น DependencyProperty
— Josh G

10
คำถามที่ดี แต่ฉันไม่เข้าใจคำตอบที่ยอมรับ ฉันจะขอบคุณถ้ากูรู WPF บางคนสามารถสอนฉันได้มากขึ้น - นี่เป็นข้อบกพร่องหรือตามการออกแบบ
— Oskar

@Oskar ตามนี้มันบั๊ก ไม่มีการแก้ไขในสายตาแม้ว่า
— user1151923

คำตอบ:


46

ผลการวิจัยบางส่วนของ 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 (คุณสมบัติการอ้างอิงแบบ True read-only)

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

Reflector ให้คำตอบ:

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

30
นั่นคือจุดบกพร่องจริงๆ
— Inferis

การวิจัยที่ดี ถ้าคุณไม่ได้วางมันไว้ที่นี่อย่างดีฉันคงต้องเดินไปตามเส้นทางที่เจ็บปวดแบบนั้น เห็นด้วยกับ @Inferis
— kevinarpe

1
นี่คือบั๊กหรือไม่? เหตุใดการเชื่อม OneWayToSource จึงไม่ได้รับอนุญาตด้วย DependencyProperty แบบอ่านอย่างเดียว
— Alex Hope O'Connor

นี่ไม่ใช่บั๊ก โดยการออกแบบและการจัดทำเอกสารอย่างดี เป็นเพราะวิธีที่เอ็นจิ้นการรวมทำงานร่วมกับระบบคุณสมบัติการพึ่งพา (เป้าหมายการโยงต้องเป็นDependencyPropertyDP) DP แบบอ่านอย่างเดียวสามารถแก้ไขได้โดยใช้ที่เกี่ยวข้องDependencyPropertyKeyเท่านั้น ในการลงทะเบียนBindingExpressionเครื่องยนต์จะต้องจัดการข้อมูลเมตาของ DP เป้าหมาย เนื่องจาก DependencyPropertyKeyถือว่าเป็นส่วนตัวเพื่อรับประกันการป้องกันการเขียนสาธารณะเอ็นจินจะต้องละเว้นคีย์นี้เนื่องจากไม่สามารถลงทะเบียนการผูกกับ DP แบบอ่านอย่างเดียว
— BionicCode

23

นี่เป็นข้อ จำกัด ของ WPF และเป็นไปตามการออกแบบ มีการรายงานเกี่ยวกับ Connect ที่นี่: การเชื่อม
OneWayToSource จากคุณสมบัติการอ้างอิงแบบอ่านอย่างเดียว

ผมทำวิธีการแก้ปัญหาแบบไดนามิกจะสามารถผลักดันอ่านอย่างเดียวคุณสมบัติการพึ่งพาแหล่งที่มาที่เรียกว่าPushBindingที่ฉันblogged เกี่ยวกับที่นี่ ตัวอย่างด้านล่างนี้เชื่อมOneWayToSourceโยงจาก DP แบบอ่านอย่างเดียว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ทำงานโดยใช้คุณสมบัติ Dependency สองตัว Listener และ Mirror Listener ถูกผูกไว้OneWayกับ TargetProperty และในPropertyChangedCallbackนั้นจะอัพเดตคุณสมบัติ Mirror ซึ่งถูกผูกไว้OneWayToSourceกับสิ่งที่ระบุไว้ใน Binding

สามารถดาวน์โหลดโครงการสาธิตได้ที่นี่
ประกอบด้วยซอร์สโค้ดและตัวอย่างการใช้งานสั้น ๆ


น่าสนใจ! ฉันคิดวิธีแก้ปัญหาที่คล้ายกันและเรียกมันว่า "Conduit" - Conduit มีคุณสมบัติการพึ่งพาสองแบบตามการออกแบบของคุณและการผูกสองแบบแยกกัน กรณีการใช้งานที่ฉันมีคือการผูกคุณสมบัติเก่าธรรมดากับคุณสมบัติเก่าธรรมดาใน XAML
— Daniel Paull

3
ฉันเห็นว่าลิงก์ MS Connect ของคุณใช้ไม่ได้อีกต่อไป หมายความว่า MS ได้แก้ไขแล้วใน. NET เวอร์ชันใหม่กว่าหรือเพิ่งลบออกไป?
— จิ๋ว

@Tiny Connect ดูเหมือนจะถูกทิ้งไปในที่สุดน่าเสียดาย มีการเชื่อมโยงไปยังสถานที่ต่างๆ ฉันไม่คิดว่ามันมีความหมายเป็นพิเศษเกี่ยวกับว่าปัญหาได้รับการแก้ไขหรือไม่
— UuDdLrLrSs

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

ยังไม่ได้ทดสอบในรูปแบบและเทมเพลตเดาว่าต้องใช้ปลอกพิเศษ


2

นี่คืออีกหนึ่งโซลูชันคุณสมบัติที่แนบมาตาม 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);
    }
}

ประกาศคุณสมบัติที่แนบมาในการควบคุม

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

1

นี่คือการนำไปใช้งานอื่นสำหรับการผูกกับ 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

<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


0

WPF จะไม่ใช้ตัวตั้งค่าคุณสมบัติ CLR แต่ดูเหมือนว่าจะมีการตรวจสอบความถูกต้องแปลก ๆ ตามมัน

อาจอยู่ในสถานการณ์ของคุณสิ่งนี้ก็โอเค:

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

1
ไม่ได้ใช้คุณสมบัติ CLR ในกรณีนี้
— Inferis

คุณหมายความว่าคุณเพิ่งกำหนด DependencyProperty และสามารถเขียน <controls: FlagThingy IsModified = "... " /> ได้หรือไม่ สำหรับฉันมันบอกว่า: "คุณสมบัติ 'IsModified' ไม่มีอยู่ในเนมสเปซ XML" ถ้าฉันไม่เพิ่มคุณสมบัติ CLR
— alex2k8

1
ฉันเชื่อว่าเวลาออกแบบใช้คุณสมบัติ clr โดยที่รันไทม์จะไปที่คุณสมบัติการพึ่งพาโดยตรง (ถ้าเป็นอย่างใดอย่างหนึ่ง)
— meandmycode

คุณสมบัติ CLR ไม่จำเป็นในกรณีของฉัน (ฉันไม่ได้ใช้ IsModified จากโค้ด) แต่ก็ยังคงมีอยู่ (มีเฉพาะตัวตั้งค่าสาธารณะ) ทั้ง designtime และ runtime ทำงานได้ดีด้วยการลงทะเบียน dependencyproperty
— Inferis

การผูกเองไม่ได้ใช้คุณสมบัติ CLR แต่เมื่อคุณกำหนดการผูกใน XAML จะต้องถูกแปลเป็นรหัส ฉันเดาว่าในขั้นตอนนี้ตัวแยกวิเคราะห์ XAML จะเห็นว่าคุณสมบัติ IsModified เป็นแบบอ่านอย่างเดียวและแสดงข้อยกเว้น (ก่อนที่จะสร้างการเชื่อมโยง)
— alex2k8

0

อืม ... ฉันไม่แน่ใจว่าฉันเห็นด้วยกับวิธีแก้ปัญหาเหล่านี้ วิธีการระบุการเรียกกลับบีบบังคับในการลงทะเบียนทรัพย์สินของคุณที่ละเว้นการเปลี่ยนแปลงภายนอก ตัวอย่างเช่นฉันต้องการใช้คุณสมบัติการอ้างอิงตำแหน่งแบบอ่านอย่างเดียวเพื่อรับตำแหน่งของตัวควบคุม 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 ส่วนตัว


น่าเสียดายที่สิ่งนี้ใช้ไม่ได้กับคุณสมบัติที่กำหนดไว้ล่วงหน้าของคลาส BCL: - /
— หรือ Mapper

0

วิธีที่ฉันแก้ไขข้อ จำกัด นี้คือการเปิดเผยเฉพาะคุณสมบัติ Binding ในชั้นเรียนของฉันโดยทำให้ DependencyProperty เป็นส่วนตัวโดยสิ้นเชิง ฉันใช้คุณสมบัติเขียนอย่างเดียว "PropertyBindingToSource" (อันนี้ไม่ใช่ DependencyProperty) ซึ่งสามารถตั้งค่าเป็นค่าการผูกใน xaml ใน setter สำหรับคุณสมบัติแบบเขียนอย่างเดียวนี้ฉันเรียกไปที่ BindingOperations.SetBinding เพื่อเชื่อมโยงการโยงกับ DependencyProperty

สำหรับตัวอย่างเฉพาะของ OP จะมีลักษณะดังนี้:

การใช้งาน FlatThingy:

public partial class FlatThingy : UserControl
{
    public FlatThingy()
    {
        InitializeComponent();
    }

    public Binding IsModifiedBindingToSource
    {
        set
        {
            if (value?.Mode != BindingMode.OneWayToSource)
            {
                throw new InvalidOperationException("IsModifiedBindingToSource must be set to a OneWayToSource binding");
            }

            BindingOperations.SetBinding(this, IsModifiedProperty, value);
        }
    }

    public bool IsModified
    {
        get { return (bool)GetValue(IsModifiedProperty); }
        private set { SetValue(IsModifiedProperty, value); }
    }

    private static readonly DependencyProperty IsModifiedProperty =
        DependencyProperty.Register("IsModified", typeof(bool), typeof(FlatThingy), new PropertyMetadata(false));

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        IsModified = !IsModified;
    }
}

สังเกตว่าวัตถุ DependencyProperty แบบอ่านอย่างเดียวคงเป็นส่วนตัว ในการควบคุมฉันได้เพิ่มปุ่มที่ Button_Click จัดการการคลิก การใช้ตัวควบคุม FlatThingy ใน 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>

โปรดทราบว่าฉันได้ใช้ ViewModel สำหรับการเชื่อมโยงกับที่ไม่ได้แสดงไว้ที่นี่ มันแสดง DependencyProperty ชื่อ "FlagIsModified" ที่คุณสามารถรวบรวมได้จากแหล่งที่มาด้านบน

มันใช้งานได้ดีช่วยให้ฉันสามารถส่งข้อมูลกลับไปยัง ViewModel จากมุมมองในลักษณะที่ควบคู่กันไปอย่างหลวม ๆ โดยมีการกำหนดทิศทางของกระแสข้อมูลนั้นอย่างชัดเจน


-1

ตอนนี้คุณกำลังทำการผูกในทิศทางที่ไม่ถูกต้อง OneWayToSource จะพยายามและอัปเดต FlagIsModified บนคอนเทนเนอร์เมื่อใดก็ตามที่ IsModified เปลี่ยนแปลงตัวควบคุมที่คุณกำลังสร้าง คุณต้องการสิ่งที่ตรงกันข้ามคือมี IsModified ผูกกับ container.FlagIsModified สำหรับสิ่งนั้นคุณควรใช้โหมดการผูก OneWay

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

รายชื่อสมาชิกการแจงนับทั้งหมด: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx


5
ไม่ฉันต้องการสถานการณ์ที่คุณอธิบายว่าฉันไม่ต้องการทำ FlagThingy.IsModified -> container.FlagIsModified
— Inferis

3
การถูกทำเครื่องหมายลงเนื่องจากผู้ถามมีคำถามที่คลุมเครือดูเหมือนจะมากไปหน่อย
— JaredPar

6
@JaredPar: ฉันไม่เห็นว่ามีอะไรคลุมเครือเกี่ยวกับคำถาม คำถามระบุว่า 1) มีคุณสมบัติการพึ่งพาแบบอ่านอย่างเดียวIsIsModified2) OP ต้องการประกาศการผูกกับคุณสมบัตินั้นใน XAML และ 3) การโยงควรจะทำงานในOneWayToSourceโหมด โซลูชันของคุณใช้งานไม่ได้จริงเนื่องจากตามที่อธิบายไว้ในคำถามคอมไพลเลอร์จะไม่อนุญาตให้คุณประกาศการเชื่อมโยงกับคุณสมบัติแบบอ่านอย่างเดียวและไม่สามารถใช้งานได้ตามแนวคิดเนื่องจากIsModifiedเป็นแบบอ่านอย่างเดียวจึงไม่สามารถระบุค่าได้ เปลี่ยนแปลง (โดยการผูก)
— หรือผู้ทำแผนที่
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.