ItemSource для Listbox переопределяет itemsource для вложенного Combobox
Мой combobox использует itemsource который указан для listbox, как я могу переписать код, чтобы он обращался к другим данным из ViewModel ?
<Grid>
<ScrollViewer>
<StackPanel Orientation="Vertical">
<ListBox ItemsSource="{Binding Orders}" x:Name="LboxProducts" Width="650" Background="Transparent" BorderBrush="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem BorderBrush="Black" Background="White" Width="600">
<StackPanel Orientation="Vertical">
<TextBlock Text="" x:Name="TxtProductPath" Style="{StaticResource mainTxtb}"/>
<TextBlock Text="{Binding NameOrder}" x:Name="TxtTitle" Style="{StaticResource mainTxtb}"/>
<TextBlock Text="{Binding Description}" x:Name="TxtDescription" Style="{StaticResource mainTxtb}"/>
<ComboBox SelectedValue="{Binding OrderStatusId, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="OrderStatusId" DisplayMemberPath="StatusName" ItemsSource="{Binding RelativeSource={}}" x:Name="Combobox1" Style="{StaticResource mainCmbx}" SelectionChanged="Combobox1_SelectionChanged"/>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</ScrollViewer>
<StackPanel>
<Button Command="{Binding Save}" x:Name="BtnSave" Content="Сохранить" Style="{StaticResource mainBtn}"/>
</StackPanel>
Вот код ViewModel, OrderStatuses таблица связанная с Orders по ключу OrderStatusId так же хотелось бы чтобы Кнопка Save так же была в каждом ListBoxItem
internal class PageOrdersViewModel
{
private bool needToCheck = true;
private List<Order> orders = ConnectToDb.db.Orders.ToList();
public List<Order> Orders
{
get => orders;
set => orders = value;
}
private void ValidateProperty<T>(T value, string propertyName)
{
if (needToCheck)
{
Validator.ValidateProperty(value, new ValidationContext(this, null, null)
{
MemberName = propertyName
});
}
}
public IEnumerable<OrderStatus> OrderStatuses => ConnectToDb.db.OrderStatuses.ToList();
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public RelayCommand save;
public RelayCommand Save
{
get
{
return save ?? (new RelayCommand(obj =>
{
try
{
if (needToCheck != null)
{
ConnectToDb.db.SaveChanges();
SupplyMethods.SetMessageToStatusBar("Успешно изменено");
}
}
catch (Exception ex)
{
SupplyMethods.SetMessageToStatusBar($"Ошибка добавления. {ex.Message}");
}
}));
}
}
}
Ответы (1 шт):
Так же вы можете использовать vm как ресурс для текущего контекста. Это не является практикой для mvvm, в комментариях более подробное обсуждение. И как в примере vm это ресурс view, то при каждом создание окна будет создана новая vm.
<Window x:Class="Users"
------------------
DataContext="{DynamicResource vm}">
<Window.Resources>
<vm:PageOrdersViewModel x:Key="vm"/>
</Window.Resources>
<ListBox ItemsSource="{Binding Orders}" x:Name="LboxProducts" Width="650" Background="Transparent" BorderBrush="Transparent">
<ListBox.ItemTemplate>
<DataTemplate>
<ListBoxItem BorderBrush="Black" Background="White" Width="600">
<StackPanel Orientation="Vertical">
<TextBlock Text="" x:Name="TxtProductPath" Style="{StaticResource mainTxtb}"/>
<TextBlock Text="{Binding NameOrder}" x:Name="TxtTitle" Style="{StaticResource mainTxtb}"/>
<TextBlock Text="{Binding Description}" x:Name="TxtDescription" Style="{StaticResource mainTxtb}"/>
<ComboBox SelectedValue="{Binding OrderStatusId, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="OrderStatusId" DisplayMemberPath="StatusName" ItemsSource="{Binding OrderStatuses , Source={StaticResource vm}}" x:Name="Combobox1" Style="{StaticResource mainCmbx}" SelectionChanged="Combobox1_SelectionChanged"/>
</StackPanel>
</ListBoxItem>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Подобное используется по типу локатора. Так же вам ранее описали как при помощи восходящих привязок обратиться к DataContext.
так же хотелось бы чтобы Кнопка Save так же была в каждом ListBoxItem
Вы можете сделать что то подобное этому.
<ListBoxItem BorderBrush="Black" Background="White" Width="600">
<StackPanel Orientation="Vertical">
<TextBlock Text="" x:Name="TxtProductPath" Style="{StaticResource mainTxtb}"/>
<TextBlock Text="{Binding NameOrder}" x:Name="TxtTitle" Style="{StaticResource mainTxtb}"/>
<TextBlock Text="{Binding Description}" x:Name="TxtDescription" Style="{StaticResource mainTxtb}"/>
<ComboBox SelectedValue="{Binding OrderStatusId, UpdateSourceTrigger=PropertyChanged}" SelectedValuePath="OrderStatusId" DisplayMemberPath="StatusName" ItemsSource="{Binding OrderStatuses , Source={StaticResource vm}}" x:Name="Combobox1" Style="{StaticResource mainCmbx}" SelectionChanged="Combobox1_SelectionChanged"/>
<Button Command="{Binding Save, Source={StaticResource vm}}" x:Name="BtnSave" Content="Сохранить" Style="{StaticResource mainBtn}"/>
</StackPanel>
</ListBoxItem>
Но при вызове команды SelectedItem будет пустой если кликнуть сразу на кнопку без выбора Item, и может возникнуть ситуация к примеру если у вас была бы не кнопка сохранить, а кнопка удалить. В этом случае выбор Item был бы лишним действием, а без этого действия команда работала бы криво. Тогда решения могут быть следующими Передать в команду параметр с текущим Item
<Button Command="{Binding Save, Source={StaticResource vm}}" CommandParametr="{Binding}" x:Name="BtnSave" Content="Сохранить" Style="{StaticResource mainBtn}"/>
public RelayCommand Save
{
get
{
return save ?? (new RelayCommand(obj =>
{
try
{
var temp = obj as Order;
if(temp != null)
{
//Какая то обработка.
}
if (needToCheck != null)
{
ConnectToDb.db.SaveChanges();
SupplyMethods.SetMessageToStatusBar("Успешно изменено");
}
}
catch (Exception ex)
{
SupplyMethods.SetMessageToStatusBar($"Ошибка добавления. {ex.Message}");
}
}));
}
}
Или же вы можете использовать PreviewGotKeyboardFocus чтобы при фокусе на кнопку SelectedItem(у) были присвоены данные. Я бы использовал этот вариант так как он более рационален. Вот готовые примеры: