WPF列表框未更新
本文关键字:更新 列表 WPF | 更新日期: 2023-09-27 18:05:42
我在这个论坛搜索,但我无法找到一个解决方案,为我的具体情况。
我正在尝试理解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 Code Behind使用):
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主视图(主菜单)加载一个自定义用户控件(ExplorerView),使用以下XAML代码(包含一个列表框,每个列表框项包含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();
}
}
}
我有两个问题:
1)正如你所看到的,在MainView中我实现了一个Button click事件,它调用了ServersViewModel.ChangeServerNames()方法。问题是,我的TextBlock在explorerviewcontrol不显示更新的数据。
我ChangeServerNames()我也使用一个消息框来显示值之前和之后的变化,我看到的值正在变化,不知道为什么ListBox/TextBlock不更新…!!(我已经测试了许多其他可能的解决方案,但我不能让它工作…)
2)我读到MainView(和所有其他视图)中的CodeBehind应该只包含InitializeComponent();和"DataContext = context;"在最大…如果这是真的,按钮点击和其他事件应该放在哪里?
最后是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>
好了,我找到问题了…
代替
set { name = value; OnPropertyChanged(Name); }
set { ipAddress = value; OnPropertyChanged(IPAddress); }
我在方法调用时缺少String参数的引号正确的形式是
set { name = value; OnPropertyChanged("Name"); }
set { ipAddress = value; OnPropertyChanged("IPAddress"); }
编译器竟然没有抛出任何错误....该方法private void OnPropertyChanged(string propertyName)
无论如何,避免这些错误(我发现)的最好方法是这样写事件(调用者提供它自己的公共名称):private void OnPropertyChanged([CallerMemberName] String propertyName = "")
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
现在我可以写
set { name = value; OnPropertyChanged(); }
set { ipAddress = value; OnPropertyChanged(); }
谢谢。