![](/img/trans.png)
[英]Combobox SelectedItem doesn't update when ItemsSource changes MVVM
[英]UI not update when ItemsSource change in MVVM
所以我的應用程序有一個主 window 顯示食譜列表,第二個 window (從主窗口打開)將新食譜添加到列表中(帶有信息)。 兩者 windows 具有相同的視圖 model。 測試后新配方成功添加到列表中,但是 UI 沒有更新新配方。 我在每個配方的主 window 中也有一個刪除按鈕,它工作正常。 刪除的配方在視圖 model 和 UI 中被刪除。 我的代碼很長。 我希望它是可讀的。 這是我的代碼(還有很多其他 UI,所以我只顯示相關代碼):
window主視圖
<!--Button to open second window-->
<Button Command="{Binding OpenAddRecipeWindowCommand}" Name="AddRecipeButton" Grid.Column="0" Margin="5" Padding="5" FontSize="15" Content="Add new recipe"/>
<!--The list-->
<ListView Name="RecipeList" Grid.Row="2" Margin="5" ItemsSource="{Binding RecipeModels}">
<!-- Set the style for item container to stretch to full width-->
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<!-- Template of each item in the ListView -->
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Name="RecipeName" Grid.Column="0" FontSize="15" Margin="5" VerticalAlignment="Center" Text="{Binding Name}"/>
<Button Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.DeleteRecipeCommand}"
CommandParameter="{Binding}"
Name="Delete" Grid.Column="1" FontSize="15" Margin="5" Padding="5" HorizontalAlignment="Right" Content="Delete"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
二 window 查看
<DockPanel Margin="5">
<!-- Recipe information -->
<StackPanel DockPanel.Dock="Top">
<!-- Name, amount, price input-->
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="5 5 0 5" Padding="5" FontSize="15" VerticalAlignment="Center" Text="Tên"/>
<TextBox Grid.Column="1" Margin="0 5 5 5" Padding="2" VerticalAlignment="Center" Text="{Binding NewRecipe.Name}" FontSize="13"/>
<TextBlock Grid.Column="2" Margin="5 5 0 5" Padding="5" FontSize="15" VerticalAlignment="Center" Text="Số lượng"/>
<TextBox Grid.Column="3" Margin="0 5 5 5" Padding="2" VerticalAlignment="Center" Text="{Binding NewRecipe.Amount}" FontSize="13" MaxLength="9" PreviewTextInput="NumberValidationTextBox" />
<TextBlock Grid.Column="4" Margin="5 5 0 5" Padding="5" FontSize="15" VerticalAlignment="Center" Text="Giá thành"/>
<TextBox Grid.Column="5" Margin="0 5 5 5" Padding="2" VerticalAlignment="Center" Text="{Binding NewRecipe.Price}" FontSize="13" MaxLength="9" PreviewTextInput="NumberValidationTextBox" />
</Grid>
<!-- Checkbox and add ingredient button -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Margin="10 5 10 5" VerticalContentAlignment="Center" VerticalAlignment="Center" FontSize="15" Content="Use as ingredient"/>
<Button Grid.Row="1" Margin="10 5 10 5" Padding="5" FontSize="15" HorizontalAlignment="Left" Command="{Binding AddIngredientCommand}" Content="Add new ingredient"/>
</Grid>
</StackPanel>
<!-- ListView and buttons -->
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<ListView Name="IngredientList" ItemsSource="{Binding Ingredients}" Grid.Row="0" Margin="5" >
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
</Style>
</ListView.ItemContainerStyle>
<!-- ListViewItem template -->
<ListView.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition Width="Auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="5 5 0 5" Padding="5" FontSize="13" Text="Name"/>
<TextBox Grid.Column="1" Margin="0 5 5 5" Padding="5" MaxLength="37" FontSize="13"/>
<TextBlock Grid.Column="2" Margin="5 5 0 5" Padding="5" FontSize="13" Text="Amount"/>
<TextBox Grid.Column="3" Margin="0 5 5 5" Padding="5" MaxLength="9" FontSize="13"/>
<TextBlock Grid.Column="4" Margin="5 5 0 5" Padding="5" FontSize="13" Text="Unit"/>
<TextBox Grid.Column="5" Margin="0 5 5 5" Padding="5" MaxLength="38" FontSize="13"/>
<TextBlock Grid.Column="6" Margin="5 5 0 5" Padding="5" FontSize="13" Text="Price"/>
<TextBox Grid.Column="7" Margin="0 5 5 5" Padding="5" MaxLength="9" FontSize="13"/>
<Button Grid.Column="8" Name="DeleteItemButton" Margin="5" Padding="5"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.DeleteIngredientCommand}"
CommandParameter="{Binding}"
FontSize="13" Content="Delete"/>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<!-- Buttons -->
<DockPanel Grid.Row="1">
<Button DockPanel.Dock="Right" Name="SaveButton" Margin="5" Padding="20 5 20 5" Command="{Binding SaveRecipeCommand}" FontSize="15" Content="Save"/>
<Button DockPanel.Dock="Left" Name="CancelButton" Margin="5" Padding="20 5 20 5" HorizontalAlignment="Left" Command="{Binding CancelCommand}" FontSize="15" Content="Cancel"/>
</DockPanel>
</Grid>
</DockPanel>
查看 Model
public class MainWindowViewModel : INotifyPropertyChanged
{
/// <summary>
/// Implementation for Main window UI.
/// </summary>
#region Main Window View Model
// List of recipes which is the ItemsSource for ListViewItem
#region ObservableCollection<RecipeModel> recipeModels
private ObservableCollection<RecipeModel> recipeModels;
public ObservableCollection<RecipeModel> RecipeModels
{
get
{
return recipeModels;
}
set
{
if (recipeModels != value)
{
recipeModels = value;
RaisePropertyChanged(nameof(recipeModels));
}
}
}
#endregion
#region Buttons
// Add Recipe Window Button
public RelayCommand OpenAddRecipeWindowCommand { get; set; }
private void ExecuteOpenAddRecipeWindowCommand()
{
RecipeWindow newWindow = new RecipeWindow();
newWindow.ShowDialog();
}
// Add Ingredient Window Button
public RelayCommand OpenAddIngredientWindowCommand { get; set; }
private void ExecuteOpenAddIngredientWindowCommand()
{
RecipeWindow newWindow = new RecipeWindow();
newWindow.ShowDialog();
}
// Product Calculation Window Button
public RelayCommand OpenProductCalculationWindowCommand { get; set; }
private void ExecuteOpenProductCalculationWindowCommand()
{
RecipeWindow newWindow = new RecipeWindow();
newWindow.ShowDialog();
}
// Product Calculation Window Button
public RelayCommand OpenIngredientCalculationWindowCommand { get; set; }
private void ExecuteOpenIngredientCalculationWindowCommand()
{
RecipeWindow newWindow = new RecipeWindow();
newWindow.ShowDialog();
}
// Delete Recipe Command
public RelayCommand<object> DeleteRecipeCommand { get; set; }
private void ExecuteDeleteRecipeCommand(object recipe)
{
RecipeModels.Remove((RecipeModel)recipe);
Save();
}
#endregion
#endregion
/*------------------------------------------------------------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------------------------------------------------------------*/
/// <summary>
/// Implementation for Recipe window UI
/// </summary>
#region Recipe Window View Model
// List of ingredients which is ItemsSource for ListView
#region ObservableCollection<RecipeModel> ingredients
private ObservableCollection<IngredientModel> ingredients = new ObservableCollection<IngredientModel>();
public ObservableCollection<IngredientModel> Ingredients
{
get
{
return ingredients;
}
set
{
if (ingredients != value)
{
ingredients = value;
RaisePropertyChanged(nameof(ingredients));
}
}
}
#endregion
// The recipe to be newly created, or edited.
#region RecipeModel newRecipe
private RecipeModel newRecipe = new RecipeModel();
public RecipeModel NewRecipe
{
get
{
return newRecipe;
}
set
{
if (newRecipe != value)
{
newRecipe = value;
}
RaisePropertyChanged(nameof(newRecipe));
}
}
#endregion
#region Buttons
// Cancel command
public RelayCommand CancelCommand { get; set; }
public Action CloseWindow { get; set; }
public void ExecuteCancelCommand()
{
CloseWindow();
}
// Add ingredient command
public RelayCommand AddIngredientCommand { get; set; }
public void ExecuteAddIngredientCommand()
{
Ingredients.Add(new IngredientModel());
}
// Delete ingredient command
public RelayCommand<object> DeleteIngredientCommand { get; set; }
public void ExecuteDeleteIngredientCommand(object ingredient)
{
Ingredients.Remove((IngredientModel)ingredient);
}
/* THIS IS THE PART WHERE I TRY TO SAVE THE NEW RECIPE TO THE LIST BUT UI DOES NOT CHANGE */
// Save command
public RelayCommand SaveRecipeCommand { get; set; }
public void ExecuteSaveRecipeCommand()
{
NewRecipe.ingredients = Ingredients;
RecipeModels.Add(NewRecipe);
/* I TESTED WITH A MESSAGE BOX TO SHOW THAT THE NEW RECIPE IS ADDED */
string res = "";
foreach (var item in RecipeModels)
{
res += item.Name + " ";
}
MessageBox.Show(res);
CloseWindow();
}
#endregion
#endregion
/*------------------------------------------------------------------------------------------------------------------------------------*/
/*------------------------------------------------------------------------------------------------------------------------------------*/
/// <summary>
/// Mutual Section
/// </summary>
#region Constructor
public MainWindowViewModel(Action action)
{
// Load data from file
Load();
CloseWindow = action;
#region Main Window buttons
OpenAddRecipeWindowCommand = new RelayCommand(ExecuteOpenAddRecipeWindowCommand);
OpenAddIngredientWindowCommand = new RelayCommand(ExecuteOpenAddIngredientWindowCommand);
OpenProductCalculationWindowCommand = new RelayCommand(ExecuteOpenProductCalculationWindowCommand);
OpenIngredientCalculationWindowCommand = new RelayCommand(ExecuteOpenIngredientCalculationWindowCommand);
DeleteRecipeCommand = new RelayCommand<object>(ExecuteDeleteRecipeCommand);
#endregion
#region Add Ingredient Window buttons
CancelCommand = new RelayCommand(ExecuteCancelCommand);
SaveRecipeCommand = new RelayCommand(ExecuteSaveRecipeCommand);
AddIngredientCommand = new RelayCommand(ExecuteAddIngredientCommand);
DeleteIngredientCommand = new RelayCommand<object>(ExecuteDeleteIngredientCommand);
#endregion
}
#endregion
#region Property Change Notification
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
#endregion
編輯:對不起,我忘記在后面包含代碼。 所以這就是我設置 windows 的 DataContext 的方式:
主營Window
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new MainWindowViewModel(null);
}
}
第二 Window
public partial class RecipeWindow : Window
{
public RecipeWindow()
{
InitializeComponent();
this.DataContext = new MainWindowViewModel(this.Close);
}
}
也許我已經為每個 window 創建了視圖 model 的兩個實例。 從這里我該如何解決?
兩者 windows 具有相同的視圖 model。
我懷疑他們這樣做。 我認為他們有自己的相同 class 實例。
您可以嘗試在打開 window 時設置它的DataContext
:
private void ExecuteOpenAddRecipeWindowCommand()
{
RecipeWindow newWindow = new RecipeWindow();
newWindow.DataContext = this;
newWindow.ShowDialog();
}
那么 windows 應該有相同的DataContext
。
另請注意,在視圖 model 中直接創建 windows 有效地破壞了 MVVM 設計模式。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.