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 шт):

Автор решения: xellan

Так же вы можете использовать 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(у) были присвоены данные. Я бы использовал этот вариант так как он более рационален. Вот готовые примеры:

  1. Использование Attached Property
  2. Использование тригера для PreviewGotKeyboardFocus
→ Ссылка