【问题标题】:Binding different properties within WPF element using different DataContexts [closed]使用不同的 DataContexts 在 WPF 元素中绑定不同的属性 [关闭]
【发布时间】:2022-01-18 06:01:14
【问题描述】:

这部分代码用于更新 C# WPF 中文本框中数字的显示。

CS:

namespace BindTest
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public BindInt x1 = new BindInt(0);

        public MainWindow()
        {
            InitializeComponent();
            Test1_Tbox.DataContext = x1;
        }

        private void Up_Btn_Click(object sender, RoutedEventArgs e)
        {
            x1.Number += 1;
        }

        private void Dn_Btn_Click(object sender, RoutedEventArgs e)
        {
            x1.Number -= 1;
        }
    }

    public class BindInt : INotifyPropertyChanged
    {
        // integer class with property change notification for binding

        private int number;

        public event PropertyChangedEventHandler PropertyChanged;

        public BindInt()
        {
            number = 0;
            OnPropertyChanged("Number");
        }

        public BindInt(int Val)
        {
            number = Val;
            OnPropertyChanged("Number");
        }

        public int Number
        {
            get { return this.number; }
            set
            {
                if (number != value)
                {
                    number = value;
                    OnPropertyChanged(nameof(Number));
                }
            }
        }

        protected void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}

XAML:

<Window x:Class="BindTest.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:BindTest"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <TextBox x:Name="Test1_Tbox" 
                 HorizontalAlignment="Left" 
                 Margin="10,10,0,0" 
                 Text="{Binding Number, Mode=OneWay, StringFormat=\{0:D\}}" 
                 TextWrapping="Wrap" 
                 VerticalAlignment="Top" Width="120"/>

        <Button x:Name="Up_Btn" Click="Up_Btn_Click" Content="Up" HorizontalAlignment="Left" Margin="550,124,0,0" VerticalAlignment="Top" Width="50" Height="20"/>
        <Button x:Name="Dn_Btn" Click="Dn_Btn_Click" Content="Down" HorizontalAlignment="Left" Margin="550,164,0,0" VerticalAlignment="Top" Width="50" Height="20"/>

    </Grid>
</Window>

所以现在我可以在单击按钮时看到显示的值更新。

在我的实际应用程序中,我需要执行以下操作:

  • 显示值如上。
  • 使多个控件可见或隐藏以及启用或禁用。

目前,我只是直接写入每个控件的可见性属性,但由于控件的数量,这变得笨拙。我正在寻找的是一种方法:

  • 将文本值绑定到一个属性(例如上例中的 x1.Number)。
  • 将可见性绑定到另一个属性。
  • 可能将 Enabled 绑定到另一个属性。

我尝试了许多绑定组合,但似乎唯一对我有用的就是上面的安排 - 在代码隐藏中设置 DataContext 并绑定到 x1 的 Number 属性。

有没有一种很好的通用方法来以这种方式绑定?如果我可以将 DataContext 设置为 MainWindow 类,然后绑定到例如 x1.Number,那么这可能会满足我的需求。

更新了我的最终解决方案:

最后,我找到了另一个更简单的答案。对于常用的可见性绑定,我使用了一个 DependencyProperty,如下所示。每个需要被公共控件设置为可见/隐藏的控件都绑定到 DIAGenable。

CS 片段:

public static readonly DependencyProperty DIAGEnableProperty =
        DependencyProperty.Register("DIAGEnable", typeof(bool),
        typeof(MainWindow), new FrameworkPropertyMetadata(true,
            FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

    public bool DIAGEnable
    {
        get
        {
            return (bool)GetValue(DIAGEnableProperty);
        }
        set
        {
            SetValue(DIAGEnableProperty, value);
        }
    }

典型 XAML:

<TextBox x:Name="RuntimeTbox" Grid.Column="1" HorizontalAlignment="Left" Height="40" Margin="385,657,0,0" 
        TextWrapping="Wrap" Text="{Binding Path=Number,  Mode=OneWay, StringFormat=\{0:F\}}" VerticalAlignment="Top" 
        Width="108" VerticalContentAlignment="Center" HorizontalContentAlignment="Center" FontSize="16" 
        FontWeight="Bold" Background="#FFF0F0F0" Focusable="False"
        Visibility="{Binding Path=DIAGEnable, Converter={StaticResource BooleanToVisibilityConverter}, ElementName=MainWin}" 
        IsEnabled="{Binding Path=DIAGEnable, ElementName=MainWin}"/>

我仍然无法在 CodeBehind 中使用这一行:

Test1_Tbox.DataContext = x1;

设置DataContext绑定x1.Number的文本值。 无论我尝试什么,我的代码都没有接受 XAML 中定义的 WPF 控件的通用 DataContext。

【问题讨论】:

  • 您的视图模型类 (BindInt) 当然可能会公开多个属性,这些属性的实现方式是,当一个属性发生变化时,其他属性也会发生变化。在属性 B 的 setter 中简单更新属性 A。“从属”属性也可以只读方式实现(只有一个 getter),其中控制属性的 setter 执行多个 OnPropertyChanged 调用。
  • 另外,您可以将多个 UI 元素绑定到单个源属性并使用绑定转换器,或者在元素样式中使用 DataTriggers。
  • 请注意,在视图模型构造函数中调用 OnPropertyChanged 是没有意义的。
  • 在已编辑的问题中查看我的最终解决方案

标签: c# wpf xaml binding


【解决方案1】:

使用 MVVM 模式 从 MainWindow 中删除所有逻辑

创建新类 Observableobject

public class ObservableObject : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

通过扩展 ICommand 创建名为 UpdateTextCommand 的命令并在那里处理您的逻辑

public class UpdateTextCommand : ICommand
{
    public event EventHandler CanExecuteChanged;
    private MainViewModel Mainviewmodel;
    public UpdateTextCommand(MainViewModel mainviewmodel)
    {
       Mainviewmodel = mainviewmodel
    }

    public bool CanExecute(object parameter)
    {
        return true;
    }

    public void Execute(object parameter)
    {
       Mainviewmodel.SomeText = "Changed Text";
       //Visibility.Collapsed hides controls
       Mainviewmodel.Visibility = Visibility.Collapsed
    }
}

创建其他类 MainViewModel 扩展 Observableobject

public class MainViewModel : Observableobject
{
    public MainViewModel()
    {
        UpdateTextCommand= new UpdateTextCommand(this);
    }
    public ICommand UpdateTextCommand{ get; }

    private Visibility _visibility;

    public Visibility Visibility
    {
        get { return _visibility; }
        set { _visibility= value;
            OnPropertyChanged(nameof(Visibility));
        }
    }

    private string _sometext;

    public string SomeText
    {
        get { return _sometext; }
        set
        {
            _sometext= value;
            OnPropertyChanged(nameof(SomeText));
        }
    }
}

现在在 MainWindow 构造函数中添加绑定

    public MainWindow()
    {
        DataContext = MainViewModel;
        InitializeComponent();
    }

在 Xaml 中,只需将绑定添加到 MainViewModel 属性

 <TextBox x:Name="Test1_Tbox" 
             HorizontalAlignment="Left" 
             Margin="10,10,0,0" 
             Text="{Binding SomeText, StringFormat=\{0:D\}}" 
             Visibility = "{Binding Visibility}"
             TextWrapping="Wrap" 
             VerticalAlignment="Top" Width="120"/>
<Button x:Name="Up_Btn" Command={Binding UpdateTextCommand}  Content="Up" HorizontalAlignment="Left" Margin="550,124,0,0" VerticalAlignment="Top" Width="50" Height="20"/>

【讨论】:

  • 虽然“扩展 ICommand 并在那里处理你的逻辑”可能会影响绑定 UI 元素的 IsEnabled 状态,但不会影响它们的可见性,因此不是要求。除此之外,你没有解释它是如何工作的。无需展示 OP 已经知道的内容,即如何实现 INotifyPropertyChanged。
  • 我添加了 UpdateTextCommand 实现,现在看到从命令更改 Visibility 字段,显示/隐藏绑定到 MainViewModel 的 Visibility 属性的所有控件
  • 我还将 IsVisibilty 类型从 bool 更改为 Visibilty,因为我们无法将 bool 属性绑定到 Visibility
猜你喜欢
  • 2011-11-06
  • 1970-01-01
  • 2020-12-22
  • 2017-09-14
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 2019-08-15
  • 1970-01-01
相关资源
最近更新 更多