【问题标题】:WPF Bind TextBlock to Specific Item of List in own ViewModelWPF 将 TextBlock 绑定到自己 ViewModel 中的特定列表项
【发布时间】:2022-01-23 21:23:09
【问题描述】:

我是 WPF 新手,在尝试将多个 TextBlocks 绑定到我的“ItemViewModel”-List 的属性时遇到了问题。

HomeView.xaml 是一个用户控件,显示在主窗口上。 我在所有数据所在的位置创建了自己的 HomeViewModel.cs,因此我不使用 HomeView.xaml.cs,也没有更改该代码中的任何内容。

我的目标是,每当“HomeViewModel.cs”中的“_items[i]._weight”或“_items[i]._count”的值发生变化时,“HomeView.xaml”中的绑定 TextBlock 或 TextBox将获得新值。

另外,例如,我如何告诉“item1WeightTBx”它绑定到“_items[0]._weight”而不是“_items[3]._weight “?

到目前为止,“HomeView.xaml”一直在“HomeView”而不是“HomeViewModel”中寻找数据。

如果您需要更多代码或信息,请告诉我 :)

提前谢谢你!

HomeView.xaml(用户控制)

<UserControl x:Class="Project.MVVM.View.HomeView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:viewModel="clr-namespace:Project.MVVM.ViewModel"
             xmlns:local="clr-namespace:Project.MVVM.View"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800">

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="109*"/>
            <RowDefinition Height="156*"/>
            <RowDefinition Height="185*"/>
        </Grid.RowDefinitions>

        <TextBox x:Name="item1WeightTBx"
                 Grid.Row="2"
                 HorizontalAlignment="Left"
                 HorizontalContentAlignment="Center"
                 VerticalContentAlignment="Center"
                 DataContext="{Binding Items}"
                 Text="{Binding _weight}"
                 Height="23" 
                 Margin="51,39,0,0"
                 TextWrapping="Wrap"
                 VerticalAlignment="Top"
                 Width="40"
                 FontSize="14"/>

        <TextBlock x:Name="item1CountTBk"
                   HorizontalAlignment="Left"
                   Grid.Row="2"
                   Height="28"
                   Margin="51,65,0,0"
                   TextWrapping="Wrap"
                   DataContext="{Binding Items}"
                   Text="{Binding _count}"
                   VerticalAlignment="Top"
                   Width="57"
                   FontSize="16"
                   Foreground="#424242"/>

        <TextBox x:Name="item2WeightTBx"
                 Grid.Row="2"
                 HorizontalAlignment="Left"
                 HorizontalContentAlignment="Center"
                 VerticalContentAlignment="Center"
                 DataContext="{Binding Items}"
                 Text="{Binding _weight}"
                 Height="23" 
                 Margin="123,39,0,0"
                 TextWrapping="Wrap"
                 VerticalAlignment="Top"
                 Width="40"
                 FontSize="14"/>

        <TextBlock x:Name="item2CountTBk"
                   HorizontalAlignment="Left"
                   Grid.Row="2"
                   Height="28"
                   Margin="123,65,0,0"
                   TextWrapping="Wrap"
                   DataContext="{Binding Items}"
                   Text="{Binding _count}"
                   VerticalAlignment="Top"
                   Width="57"
                   FontSize="16"
                   Foreground="#424242"/>

                   .
                   .
                   .

HomeViewModel.cs

        public List<ItemViewModel> _items;

        public List<ItemViewModel> Items => _items;


public HomeViewModel(Connection connectionInterface, ShelfComp shelfComp)
        {
            _connectioninterface = connectionInterface;
            _scale = new Scale("0,0;0,0", ShelfWidth);
            _shelfComp = shelfComp;
            

            for (int i = 0; i < shelfComp.ItemList.Count; i++)
            {
                _items.Add(new ItemViewModel(_shelfComp.ItemList[i]));
            } 
        }

ItemViewModel.cs

class ItemViewModel : ObservableObject
    {
        public Item _item;

        public string _name => _item.Name;
        public int _minIndex => _item.MinIndex;
        public int _maxIndex => _item.MaxIndex;
        public double _weight => _item.Weight;
        public int Width => _item.Width;
        public int _count => _item.Count;

        public ItemViewModel(Item item)
        {
            _item = item;
        }
    }

Item.cs

public class Item
    {
        public string Name { get; set; }
        public int MinIndex { get; set; }
        public int MaxIndex { get; set; }
        public double Weight { get; }
        public int Width { get; }
        public int Count { get; set; }


        public Item(string Name, int MinIndex, int Width, double Weight)
        {
            this.Name = Name;
            this.MinIndex = MinIndex;
            this.Width = Width;
            this.MaxIndex = MinIndex + Width;
            this.Weight = Weight;
            Count = 0;
        }

    }

ObservableObject.cs

class ObservableObject : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

【问题讨论】:

标签: c# wpf xaml data-binding binding


【解决方案1】:

您必须始终引发INotifyPropetyChanged.PropertyChanged 事件才能更新绑定。单独实现界面只是故事的一半。每个应该支持动态绑定的属性都必须引发事件。

当你组合一个类型并使用成员委托时,组合成员的实例必须是private。否则,委托是多余的,因为调用者可以绕过类 API 并直接改变实例甚至覆盖实例。如果你想隐藏Item,属性必须private
此外,永远不要定义 public 字段。目前ItemViewModel.Item 是一个public 字段。它必须是private 和一个属性。
public 属性或public 通常的成员,必须使用 PascalCase 命名。 _camelCase 表示法仅用于私有字段。

同样适用于HomeViewModel 成员:

public List<ItemViewModel> _items;
public List<ItemViewModel> Items => _items;

这是毫无意义的。这是不对的。支持字段必须是 private(记住:没有 public 字段!)。此外,由于这是一个只读属性(它只有一个get()),所以支持字段是多余的。您可以改用只读自动属性:

// Initialize from constructor
public List<ItemViewModel> Items { get; }

请记住,构造函数必须快速执行。构造函数中必须避免长时间运行的for 循环。

要解决您的绑定问题,请确保绑定源上的每个属性都会引发 INotifyPropetyChanged.PropertyChanged 事件:

ItemViewModel.cs

class ItemViewModel : ObservableObject
{
  private Item Item { get; }

  // Repeat this pattern for all remainig properties
  public string Name
  {
     get => Item.Name;
     set
     {
       Item.Name = value;
       OnPropertyChanged();
     }
  }

  public ItemViewModel(Item item)
  {
     Item = item;
  }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-13
    • 2010-10-08
    • 2013-09-13
    • 2015-07-04
    相关资源
    最近更新 更多