【问题标题】:Bind UI values to object instance of model in WPF将 UI 值绑定到 WPF 中模型的对象实例
【发布时间】:2018-03-29 10:02:00
【问题描述】:

我有一个简单的 wpf 项目,它由一个包含员工 ObservableCollection 的列表框和 3 个文本框组成。

我还创建了一个“模型”类,它将像 Employee 这样的属性对象链接到 MainWindow。 Employee 类的属性称为“CurrentEmployee”。

我将一个模型对象初始化为窗口并将其命名为“boundModel”。当列表框上的选择发生更改时,我希望“boundModel.CurrentEmployee”更改为选择的任何员工,并且 3 个文本框也更改为员工的属性。我目前将“boundModel.CurrentEmployee”设置为列表框中选择的任何名称。当列表框选择发生变化时,boundModel.CurrentEmployee 也会发生变化!不幸的是,它不会在 MainWindow 上更新!

我想将 boundModel.CurrentEmployee 绑定到 3 个文本框。我想这样做,这样当我创建另一个类时,我可以通过 Model 类将该类链接到更多的 UI 控件。这是否可能,或者以任何方式有效?我的窗体背景在这里似乎几乎没用!

提前致谢!

主窗口后端

public partial class MainWindow : Window
{
    Model boundModel;
    public MainWindow()
    {
        boundModel = new Model();
        InitializeComponent();
        this.DataContext = boundModel;
        lb_Employees.ItemsSource = boundModel.EmployeeCollection;
    }

    private void lb_Employees_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
        boundModel.CurrentEmployee = (Employee)lb_Employees.SelectedItem;
    }
}

XAML 主窗口

    <ListBox x:Name="lb_Employees" SelectionChanged="lb_Employees_SelectionChanged">
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    <TextBox x:Name="tb_Name" Text="{Binding boundModel.Name}"/>
    <TextBox x:Name="tb_Age" Text="{Binding Age}"/>
    <TextBox x:Name="tb_Kids" Text="{Binding HasKids}"/>

员工

class Employee : System.ComponentModel.INotifyPropertyChanged
{
    private string name;
    private int age;
    private bool hasKids;

    public string Name
    {
        get{return name;}
        set
        {
            name = value;
            Notify("Name");
        }
    }
    public int Age
    {
        get{return age;}
        set
        {
            age = value;
            Notify("Age");
        }
    }
    public bool HasKids
    {
        get{return hasKids;}
        set
        {
            hasKids = value;
            Notify("HasKids");
        }
    }

    public Employee(string name, int age, bool haskids)
    {
        this.Name = name;
        this.Age = age;
        this.HasKids = haskids;
    }
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    private void Notify(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this,
                new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }
}

型号

class Model : System.ComponentModel.INotifyPropertyChanged
{
    public ObservableCollection<Employee> EmployeeCollection = new ObservableCollection<Employee>();
    private Employee currentemployee;
    public Employee CurrentEmployee
    {
        get {return currentemployee;}
        set
        {
            currentemployee = value;
            Notify("Employee");
        }
    }
    public Model()
    {
        EmployeeCollection.Add(new Employee("Tom Smith", 40, true));
        EmployeeCollection.Add(new Employee("Beth Smith", 38, true));
        EmployeeCollection.Add(new Employee("Steph Rodriguez", 25, false));
    }
    public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
    private void Notify(string propertyName)
    {
        if (PropertyChanged != null)
            PropertyChanged(this,
                new System.ComponentModel.PropertyChangedEventArgs(propertyName));
    }
}

【问题讨论】:

    标签: c# wpf xaml data-binding listbox


    【解决方案1】:

    此属性的名称是 CurrentEmployee,而不是 Employee。

    public Employee CurrentEmployee
    {
        get {return currentemployee;}
        set
        {
            currentemployee = value;
            //Notify("Employee");
            Notify("CurrentEmployee");
        }
    }
    

    boundModel 是一个私有字段。您不能绑定到私有的任何东西,也不能绑定到任何类型的字段。

    它也是(或应该是)与DataContext 相同的对象。这些绑定转到DataContext 以查找在其路径中命名的属性。因此,如果您想绑定到DataContext.CurrentEmployee.Name,请这样做:

    <TextBox x:Name="tb_Name" Text="{Binding CurrentEmployee.Name}"/>
    <TextBox x:Name="tb_Age" Text="{Binding CurrentEmployee.Age}"/>
    <TextBox x:Name="tb_Kids" Text="{Binding CurrentEmployee.HasKids}"/>
    

    我实际上建议摆脱 boundModel 并这样做(我不会命名我的 viewmodel 模型,但你已经这样做了):

    public MainWindow()
    {
        InitializeComponent();
    
        ViewModel = new Model();
    }
    
    public Model ViewModel { 
        get { return DataContext as Model; }
        set { DataContext = value; }
    }
    

    我会在 XAML 中绑定 ItemsSource,并通过绑定设置 CurrentEmployee:

    <ListBox 
        x:Name="lb_Employees" 
        ItemsSource="{Binding EmployeeCollection}"
        SelectedItem="{Binding CurrentEmployee}"
        >
        <ListBox.ItemTemplate>
            <DataTemplate>
                <TextBlock Text="{Binding Name}"/>
            </DataTemplate>
        </ListBox.ItemTemplate>
    </ListBox>
    

    更新

    哎呀,另一种简化方式:ItemTemplate 并不足以证明其存在的合理性。您可以改用DisplayMemberPath:

    <ListBox 
        x:Name="lb_Employees" 
        ItemsSource="{Binding EmployeeCollection}"
        SelectedItem="{Binding CurrentEmployee}"
        DisplayMemberPath="Name"
        />
    

    现在我们已经放弃了代码隐藏与视图模型直接交互的所有地方,我们也可以放弃ViewModel 属性。它不再需要了。代码隐藏并不是一件坏的事情,但如果你正确地使用 WPF,你经常会发现那里几乎不需要做任何事情。

    【讨论】:

    • 谢谢!这完全符合预期。也感谢您的详细回答。你又为我节省了几个小时的研究 A+
    猜你喜欢
    • 1970-01-01
    • 2013-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多