Saya mencoba mengikat ke Readonly
properti dengan OneWayToSource
mode as, tetapi tampaknya ini tidak dapat dilakukan di XAML:
<controls:FlagThingy IsModified="{Binding FlagIsModified,
ElementName=container,
Mode=OneWayToSource}" />
Saya mendapat:
Properti 'FlagThingy.IsModified' tidak dapat disetel karena tidak memiliki pengakses kumpulan yang dapat diakses.
IsModified
adalah readonly DependencyProperty
pada FlagThingy
. Saya ingin mengikat nilai itu ke FlagIsModified
properti di penampung.
Untuk lebih jelasnya:
FlagThingy.IsModified --> container.FlagIsModified
------ READONLY ----- ----- READWRITE --------
Apakah ini mungkin hanya dengan menggunakan XAML?
Pembaruan: Ya, saya memperbaiki kasus ini dengan mengatur pengikatan pada wadah dan bukan pada FlagThingy
. Tapi saya masih ingin tahu apakah ini mungkin.
wpf
data-binding
xaml
readonly
Inferis
sumber
sumber
IsModified
ke properti readwriteFlagIsModified
.Jawaban:
Beberapa hasil penelitian untuk OneWayToSource ...
Pilihan 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);
Pilihan 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}" />
Opsi # 3 (Properti ketergantungan hanya baca yang sebenarnya)
System.ArgumentException: Properti 'IsModified' tidak dapat terikat data.
// 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 memberikan jawabannya:
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"); } ....
sumber
DependencyProperty
DP). DP hanya-baca hanya dapat dimodifikasi menggunakan yang terkaitDependencyPropertyKey
. Untuk mendaftarkanBindingExpression
mesin harus memanipulasi meta data DP target. KarenaDependencyPropertyKey
dianggap privat untuk menjamin perlindungan tulis publik, mesin harus mengabaikan kunci ini sehingga tidak dapat mendaftarkan pengikatan pada DP hanya-baca.Ini adalah batasan WPF dan memang dirancang. Ini dilaporkan di Connect di sini:
OneWayToSource mengikat dari properti ketergantungan hanya-baca
Saya membuat solusi untuk secara dinamis dapat mendorong properti ketergantungan hanya-baca ke sumber bernama
PushBinding
yang saya blogkan di sini . Contoh di bawah ini melakukanOneWayToSource
Binding dari DP read-onlyActualWidth
danActualHeight
ke properti Width dan Height dariDataContext
<TextBlock Name="myTextBlock"> <pb:PushBindingManager.PushBindings> <pb:PushBinding TargetProperty="ActualHeight" Path="Height"/> <pb:PushBinding TargetProperty="ActualWidth" Path="Width"/> </pb:PushBindingManager.PushBindings> </TextBlock>
PushBinding
bekerja dengan menggunakan dua Properti Ketergantungan, Pendengar dan Cermin. Listener terikatOneWay
ke TargetProperty dan diPropertyChangedCallback
dalamnya memperbarui properti Mirror yang terikatOneWayToSource
ke apa pun yang ditentukan di Binding.Proyek Demo dapat Diunduh Disini.
Ini berisi kode sumber dan penggunaan sampel singkat.
sumber
Tulis ini:
Pemakaian:
<TextBox Text="{Binding Text}" p:OneWayToSource.Bind="{p:Paths From={x:Static Validation.HasErrorProperty}, To=SomeDataContextProperty}" />
Kode:
Belum mengujinya dalam gaya dan template, tebak perlu casing khusus.
sumber
Berikut adalah solusi properti terlampir lainnya berdasarkan SizeObserver yang dirinci di sini Mendorong properti GUI hanya-baca kembali ke 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); } }
Deklarasikan properti terlampir dalam kontrol
<ListView ItemsSource="{Binding SomeGridItems}" ut:MouseObserver.Observe="True" ut:MouseObserver.ObservedMouseOver="{Binding IsMouseOverGrid, Mode=OneWayToSource}">
sumber
Berikut adalah implementasi lain untuk mengikat ke Validation.HasError
Penggunaan di 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>
Implementasi ini khusus untuk mengikat
Validation.HasError
sumber
WPF tidak akan menggunakan penyetel properti CLR, tetapi tampaknya melakukan beberapa validasi aneh berdasarkan itu.
Mungkin dalam situasi Anda ini bisa baik-baik saja:
sumber
Hmmm ... Saya tidak yakin saya setuju dengan salah satu solusi ini. Bagaimana dengan menentukan panggilan balik koersi dalam pendaftaran properti Anda yang mengabaikan perubahan eksternal? Misalnya, saya perlu menerapkan properti dependensi Posisi hanya-baca untuk mendapatkan posisi kontrol MediaElement di dalam kontrol pengguna. Begini cara saya melakukannya:
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); } }
Dengan kata lain, abaikan saja perubahan dan kembalikan nilai yang didukung oleh anggota lain yang tidak memiliki pengubah publik. - Dalam contoh di atas, MediaRenderer sebenarnya adalah kontrol MediaElement pribadi.
sumber
Cara saya mengatasi batasan ini adalah dengan hanya mengekspos properti Binding di kelas saya, menjaga DependencyProperty tetap pribadi sama sekali. Saya menerapkan properti hanya-tulis "PropertyBindingToSource" (yang ini bukan DependencyProperty) yang dapat disetel ke nilai yang mengikat di xaml. Dalam penyetel untuk properti hanya-tulis ini saya memanggil BindingOperations.SetBinding untuk menautkan pengikatan ke DependencyProperty.
Untuk contoh spesifik OP, akan terlihat seperti ini:
Implementasi FlatThingy:
Perhatikan bahwa objek DependencyProperty hanya baca statis bersifat privat. Dalam kontrol saya menambahkan tombol yang kliknya ditangani oleh Button_Click. Penggunaan kontrol FlatThingy di window.xaml saya:
<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>
Perhatikan bahwa saya juga telah menerapkan ViewModel untuk mengikat yang tidak ditampilkan di sini. Ini mengekspos DependencyProperty bernama "FlagIsModified" seperti yang Anda dapat dari sumber di atas.
Ini berfungsi dengan baik, memungkinkan saya untuk mendorong informasi kembali ke ViewModel dari View dengan cara yang digabungkan secara longgar, dengan arah aliran informasi yang ditentukan secara eksplisit.
sumber
Anda melakukan pengikatan ke arah yang salah sekarang. OneWayToSource akan mencoba dan memperbarui FlagIsModified pada penampung setiap kali IsModified berubah pada kontrol yang Anda buat. Anda menginginkan sebaliknya, yaitu membuat IsModified mengikat ke container.FlagIsModified. Untuk itu sebaiknya gunakan mode binding OneWay
<controls:FlagThingy IsModified="{Binding FlagIsModified, ElementName=container, Mode=OneWay}" />
Daftar lengkap anggota enumerasi: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingmode.aspx
sumber
IsIsModified
, bahwa 2) OP ingin mendeklarasikan pengikatan pada properti itu di XAML dan bahwa 3) pengikatan seharusnya bekerja dalamOneWayToSource
mode. Solusi Anda tidak berfungsi secara praktis karena, seperti yang dijelaskan dalam pertanyaan, compiler tidak akan mengizinkan Anda mendeklarasikan pengikatan pada properti hanya-baca, dan tidak berfungsi secara konseptual karenaIsModified
bersifat hanya-baca dan karenanya nilainya tidak dapat diubah (dengan mengikat).