การรวม 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.