【问题标题】:WPF ListBox Not updatingWPF ListBox 不更新
【发布时间】:2016-09-04 22:07:10
【问题描述】:

我在此论坛中进行了搜索,但找不到适合我的特定场景的解决方案。

我正在尝试了解 WPF 和 MVVM,并为此构建了一个简单的 WPF。

我的数据模型是(我在这里实现了 INotifyPropertyChanged 并且构造函数初始化了所有属性):

namespace MyApp.ui.Models
{
    public class Server : INotifyPropertyChanged
    {
        private int id;
        public int ID
        {
            get { return id; }
            set { id = value; }
        }

        private string name;
        public string Name
        {
            get { return name; }
            set { name = value; OnPropertyChanged(Name); }
        }

        private string ipAddress;
        public string IPAddress
        {
            get { return ipAddress; }
            set { ipAddress = value; OnPropertyChanged(IPAddress); }
        }

        public Server(int ServerID, string ServerName, string ServerIpAddress)
        {
            ID = ServerID;
            Name = ServerName;
            IPAddress = ServerIpAddress;
        }

        public event PropertyChangedEventHandler PropertyChanged;

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

我的 ViewModel(由 WPF 代码背后使用):

namespace MyApp.ui.ViewModels
{
    public class ServersViewModel
    {
        private ObservableCollection<Server> server;
        public ObservableCollection<Server> Servers
        {
            get { return server; }
            set { server = value; }
        }

        public ServersViewModel()
        {
            Servers = new ObservableCollection<Server>
            {
                new Server(001, "Server001", @"192.168.254.3"),
                new Server(002, "Server002", @"100.92.0.200"),
                new Server(003, "Server003", @"64.32.0.3"),
                new Server(004, "Server004", @"172.10.0.4"),
                new Server(005, "Server005", @"165.23.0.233"),
                new Server(006, "Server006", @"81.22.22.6"),
                new Server(007, "Server007", @"10.10.0.7")
            };
        }

        public void ChangeServerNames()
        {
            //Before Change
            foreach (var item in Servers)
            {
                MessageBox.Show(item.Name);
            }

            int count = 1000;

            foreach (var item in Servers)
            {
                item.Name = "Server" + count.ToString();
                count += 1000;
            }

            //After Change
            foreach (var item in Servers)
            {
                MessageBox.Show(item.Name);
            }
        }
    }
}

我的 WPF 主视图(主菜单)使用以下 XAML 代码加载自定义用户控件(ExplorerView)(包含一个列表框,每个列表框项包含 1 个复选框 + 图像 + 文本块)

<UserControl x:Class="MyApp.ui.Views.ExplorerView"
             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:local="clr-namespace:MyApp.ui.Views"
             mc:Ignorable="d" 
             d:DesignHeight="400" d:DesignWidth="200">
    <Grid>
        <ListBox ItemsSource="{Binding Servers}" Margin="2">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <CheckBox VerticalContentAlignment="Center" Margin="4">
                        <StackPanel Orientation="Horizontal">
                            <Image Source="/resources/server64.png" Height="30" Margin="4"></Image>
                            <TextBlock Text="{Binding Name}"
                                   VerticalAlignment="Center" Margin="4"></TextBlock>
                        </StackPanel>
                    </CheckBox>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

最后 MainView 代码后面加载 ServersViewModel 以便 ExplorerView 控件可以绑定数据。

namespace MyApp.ui
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public ServersViewModel context { get; set; }

        public MainWindow()
        {
            InitializeComponent();

            context = new ServersViewModel();
            DataContext = context;
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            context.ChangeServerNames();
        }
    }
}

也就是说,我有 2 个问题:

1) 如您所见,在 MainView 中,我实现了一个 Button 单击事件,该事件调用 ServersViewModel.ChangeServerNames() 方法。问题是我在 ExplorerView 控件中的 TextBlock 没有显示更新的数据。

我 ChangeServerNames() 我还使用 MessageBox 显示更改前后的值,我看到值正在更改,不知道为什么 ListBox/TextBlock 没有更新...!!! (我已经测试了许多其他可能的解决方案,但我无法让它工作......)

2) 我读到 MainView(和所有其他视图)中的 CodeBehind 应该只包含 InitializeComponent();和“数据上下文 = 上下文;”在最大... 如果是这样,按钮点击和其他事件的事件应该放在哪里?

最后是 MainWindow XAML 的代码:

<Window
        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:MyApp.ui"
        xmlns:Views="clr-namespace:MyApp.ui.Views"
        x:Class="MyApp.ui.MainWindow"
        mc:Ignorable="d"
        Title="Server" MinHeight="720" MinWidth="1024"
        Height ="720" Width="1024">
    <Grid Margin="2">

        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="200" />
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition />
        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>
            <RowDefinition Height="41*"/>
            <RowDefinition Height="608*"/>
            <RowDefinition Height="30"/>
        </Grid.RowDefinitions>

        <GridSplitter Grid.Column="1" Grid.Row="1"
              HorizontalAlignment="Center"
              VerticalAlignment="Stretch"
              Background="Gray" 
              ShowsPreview="True"
              Width="4" Margin="0,2,0,4"
              />

        <Views:MenuView Grid.ColumnSpan="3"/>
        <Views:FooterView Grid.Row="2" Grid.ColumnSpan="3" />
        <Views:ExplorerView Grid.Column="0" Grid.Row="1" />

        <!--Temp Tests-->
        <StackPanel Margin="12" Grid.Column="3" Grid.Row="1" Width="Auto" Height="Auto" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Left">
            <Button Margin="4" Width="120" Height="30" Content="Change Data Test..." Click="Button_Click" />
        </StackPanel>

    </Grid>
</Window>

感谢您的宝贵时间...

【问题讨论】:

    标签: c# wpf


    【解决方案1】:

    好的,我找到了问题...

    代替

    set { name = value; OnPropertyChanged(Name); }
    set { ipAddress = value; OnPropertyChanged(IPAddress); }
    

    我在方法调用中缺少字符串参数的引号 正确的形式是

    set { name = value; OnPropertyChanged("Name"); }
    set { ipAddress = value; OnPropertyChanged("IPAddress"); }
    

    奇怪的是编译器没有抛出任何错误......方法 私有无效 OnPropertyChanged(字符串属性名称) 正在“询问”一个字符串作为输入参数。

    避免这些错误的最好方法(我发现)是这样编写事件(调用者提供它自己的公共名称):

    private void OnPropertyChanged([CallerMemberName] String propertyName = "")
      {
        if (PropertyChanged != null)
        {
          PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
      }
    }
    

    现在我可以了

    set { name = value; OnPropertyChanged(); }
    set { ipAddress = value; OnPropertyChanged(); }
    

    谢谢。

    【讨论】:

    • IPAddress 是一个字符串。可悲的是,编译器在那里无能为力。你是对的,CallerMemberName 是正确的答案,不过——还有一个 VS sn-p 来创建类型化的视图模型属性。
    猜你喜欢
    • 2018-07-04
    • 1970-01-01
    • 2015-07-01
    • 2010-11-03
    • 1970-01-01
    • 2014-03-08
    • 2014-04-28
    • 2013-11-06
    • 2011-07-21
    相关资源
    最近更新 更多