我有兩個 UI 控制元件,我想將它們的屬性系結到兩個不同物件的屬性。這是我的 XAML 檔案:
<Window x:Class="WpfBindingDemo.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"
mc:Ignorable="d" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize">
<Canvas Width="300" Height="200">
<Slider x:Name="_slider1" Canvas.Left="10" Canvas.Top="10" Width="272"/>
<Slider x:Name="_slider2" Canvas.Left="10" Canvas.Top="36" Width="272"/>
</Canvas>
</Window>
這是我背后的代碼:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace WpfBindingDemo
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Binding binding1 = new Binding("MyProperty1");
binding1.Mode = BindingMode.TwoWay;
binding1.Source = _myObject1;
BindingOperations.SetBinding(_slider1, Slider.ValueProperty, binding1);
Binding binding2 = new Binding("MyProperty2");
binding2.Mode = BindingMode.TwoWay;
binding2.Source = _myObject1;
BindingOperations.SetBinding(_slider2, Slider.ValueProperty, binding2);
}
MyClass1 _myObject1 = new MyClass1();
MyClass2 _myObject2 = new MyClass2();
}
public class MyClass1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); }
public double MyProperty1 {get; set}
}
public class MyClass2 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); }
public double MyProperty2 {get; set}
}
}
如您所見,我將 UI 控制元件屬性(Value在本例中)系結到代碼中的不同物件屬性(在視窗建構式中)并且它作業正常,但我發現這個宣告太笨重而且我不喜歡它被分成兩部分部分。我想知道是否有更緊湊的方式來在 XAML 中宣告這種系結,比如
<Slider x:Name="_slider1" Value="{Binding MyProperty1, Source=_myObject1}"/>
<Slider x:Name="_slider2" Value="{Binding MyProperty2, Source=_myObject2}"/>
我嘗試使用Source,RelativeSource和ElementName屬性,但未能使其作業。我錯過了什么嗎?
uj5u.com熱心網友回復:
如果您將_myObject1and宣告_myObject2為屬性(pascal case),則可以系結它們。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
public MyClass1 MyObject1 { get; } = new MyClass1();
public MyClass2 MyObject2 { get; } = new MyClass2();
}
您可以使用RelativeSource來MainWindow系結它們。
<Canvas Width="300" Height="200">
<Slider Canvas.Left="10" Canvas.Top="10" Width="272" Value="{Binding MyObject1.MyProperty1, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
<Slider Canvas.Left="10" Canvas.Top="36" Width="272" Value="{Binding MyObject2.MyProperty2, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"/>
</Canvas>
您當然也可以將資料背景關系設定為視窗本身,因此您不必RelativeSource每次都使用,請參閱@Clemens 回答代碼隱藏示例。
也可以改為DataContext在 XAML 中設定。
<Window ...
DataContext="{Binding RelativeSource={RelativeSource Self}}">
然后以相同的方式簡化系結。
<Canvas Width="300" Height="200" DataContext="{Binding MainWindowViewModel}">
<Slider Canvas.Left="10" Canvas.Top="10" Width="272" Value="{Binding MyObject1.MyProperty1}"/>
<Slider Canvas.Left="10" Canvas.Top="36" Width="272" Value="{Binding MyObject2.MyProperty2}"/>
</Canvas>
盡管這可行,但這是一種不好的方法。它將用戶界面組件MainWindow與您的業務資料或邏輯混合在一起。您應該將它們分開以實作更好的可測驗性和可維護性。有一種稱為 MVVM 的常見模式專注于將視圖與資料分離。你可以在這里閱讀介紹。
您應該為您的主視窗創建一個視圖模型,通過屬性公開資料。INotifyPropertyChanged如果您打算更改屬性,您還應該在此處實作。
public class MainWindowViewModel
{
public MainWindowViewModel()
{
MyObject1 = new MyClass1();
MyObject2 = new MyClass2();
}
public MyClass1 MyObject1 { get; }
public MyClass2 MyObject2 { get; }
}
您可以DataContext直接創建和分配視圖模型。
<Window x:Class="WpfBindingDemo.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"
mc:Ignorable="d" SizeToContent="WidthAndHeight" ResizeMode="CanMinimize">
<Window.DataContext>
<local:MainWindowViewModel/>
</Window.DataContext>
<!-- ...other markup. -->
</Window>
或者,您可以創建一個實體并在代碼隱藏中分配它。
public MainWindow()
{
DataContext = this;
InitializeComponent();
}
然后系結看起來像這樣。
<Canvas Width="300" Height="200">
<Slider Canvas.Left="10" Canvas.Top="10" Width="272" Value="{Binding MyObject1.MyProperty1}"/>
<Slider Canvas.Left="10" Canvas.Top="36" Width="272" Value="{Binding MyObject2.MyProperty2}"/>
</Canvas>
作為旁注,您的實作INotifyPropertyChanged是無用的,除非您實際比較值以分配相等性并呼叫OnPropertyChanged設定器。
public class MyClass1 : INotifyPropertyChanged
{
private double _myProperty1;
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName] string name = null)
{ PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name)); }
public double MyProperty1
{
get => _myProperty1;
set
{
if (Math.Abs(_myProperty1 - value) < /* ...your comparison epsilon here. */)
return;
_myProperty1 = value;
OnPropertyChanged();
}
}
}
在浮點數的特定情況下,您應該與 epsilon 進行比較,有關詳細資訊,請參閱比較 C# 中的雙精度值。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/450810.html
下一篇:我似乎無法更改Xamarin.CommunityToolkitTabView選項卡的順序。有沒有辦法做到這一點?
