Я создаю приложение, которое, как мне казалось, работало хорошо, пока я не заметил странное поведение в элементах управления вкладками. Краткая версия состоит в том, что выбор, сделанный на одной вкладке, вызывает срабатывание подписанных событий на другой.
У меня есть главное окно с тремя пользовательскими элементами управления:
- меню, которое направляет содержимое панели выбора
- панель выбора, которая создает вкладки в рабочей области
- рабочая область - элемент управления вкладками.
В случае одного рабочий процесс, когда пользователь работает со списком элементов на левой панели, выбирает конкретный c элемент для работы, и подробности этого элемента отображаются на вкладке в рабочей области.
Каждая вкладка состоит из нескольких собственных подмоделей, представляющих сетки, составленные из модели данных продукта - у продукта есть несколько материалов (SKU). У каждого SKU есть BillOfMaterials, и т. Д. c.
Я выяснил, где, но не почему, возникает проблема.
Когда пользователь нажимает кнопку на панели для просмотра сведений об элементе он запускает опосредованное событие. Рабочая область имеет подписку на это событие, которое вызывает метод для создания новой вкладки. Я считаю, что это правильный образец.
ItemTabVM имеет в себе множество других виртуальных машин. Вот код в виртуальной машине рабочей области:
...
public _WorkAreaVM()
{
Mediator.Subscribe("newtab_item", LoadNewItemTab);
}
private void LoadNewItemTab(object obj)
{
String id = AppData.SelectedPanelValue;
if (IsTabUnique(id) == true)
TabList.Add(ItemTabVM(id));
SetSelectedTab(id);
}
...
XAML для рабочей области
...
<UserControl x:Class="CWApp.Views._WorkAreaUC"
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:CWApp.Views"
xmlns:vm="clr-namespace:CWApp.ViewModels"
mc:Ignorable="d">
<UserControl.DataContext>
<vm:_WorkAreaVM/>
</UserControl.DataContext>
<DockPanel>
<Label Content="WorkArea" />
<TabControl
ItemsSource="{Binding TabList}"
SelectedIndex="{Binding SelectedTabIndex}" >
<TabControl.Resources>
<DataTemplate DataType="{x:Type vm:ItemTabVM}">
<local:ItemUC/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:VendorTabVM}">
<local:VendorTabUC/>
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ITabViewModel}">
<TextBlock>
<Run Text="{Binding Header}"/>
<Hyperlink Command="{Binding CloseCommand}">X</Hyperlink>
</TextBlock>
</DataTemplate>
</TabControl.ItemTemplate>
</TabControl>
</DockPanel>
</UserControl>
...
XAML для каждого ItemTabU C
...
<UserControl x:Class="CWApp.Views.ItemTabUC "
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:vm="clr-namespace:CWApp.ViewModels"
xmlns:local1="clr-namespace:CWApp.Helpers"
xmlns:local="clr-namespace:CWApp.Views"
mc:Ignorable="d" >
<StackPanel>
<StackPanel.Resources>
<DataTemplate DataType="{x:Type vm:ProductMainVM}">
<local:ProductMainUC/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:MaterialGridVM}">
<local:MaterialGridUC/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:BOMGridVM}">
<local:BOMGridUC/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:RouterGridVM}">
<local:RouterGridUC/>
</DataTemplate>
<DataTemplate DataType="{x:Type vm:VendorGridVM}">
<local:VendorGridUC/>
</DataTemplate>
</StackPanel.Resources>
<ContentControl Content="{Binding ProductMain}" />
<ContentControl Content="{Binding MaterialGrid}" />
<ContentControl Content="{Binding BOMGrid}" />
<ContentControl Content="{Binding RouterGrid}" />
<ContentControl Content="{Binding VendorGrid}" />
</StackPanel>
</UserControl>
...
Один из виртуальная машина в ItemTabVM - это MaterialVM (таблица данных SKU). Когда пользователь выбирает SKU, он должен изменить BillOfMaterialVM (DataTable of Components) на основе выбранного SKU.
MaterialU C DataGrid SelectedItem привязывается к VM ...
<DataGrid Grid.Row="1" Name="dgMaterials"
ItemsSource="{Binding MaterialTable}"
SelectedItem="{Binding SelectedRow}"
AutoGenerateColumns="false">
. ..
Когда MaterialVM изменяется, он сообщает ItemTabVM, что произошло изменение. Это обрабатывается с помощью элемента управления SelectedItem datagrid, который, в свою очередь, запускает опосредованное событие.
...
public Material SelectedMaterial
{
get { return _selectedMaterial; }
set
{
_selectedMaterial = value;
OnPropertyChanged("SelectedMaterial");
NotifyMaterialChanged();
}
}
private void NotifyMaterialChanged()
{
Mediator.Notify("materialChange", true);
}
...
ItemTabVM (предок MaterialVM и BillOfMaterialVM) подписан на изменения в MaterialVM.SelectedMaterial, и при обнаружении сообщает BOMVM, какую точку данных загружать.
...
private void CreateSubscriptions()
{
Mediator.Subscribe("materialChange", SetMaterialChanged);
}
private void SetMaterialChanged(object obj)
{
String n = ThisProduct.ProductID; //error discovery
if(MaterialGrid.SelectedMaterial != null)
BOMGrid.SetGrid(MaterialGrid.SelectedMaterial);
}
...
Строка с пометкой «обнаружение ошибок» использует ProductID, загруженный в ItemTabVM, чтобы показать, какая вкладка элементов фактически активируется. Независимо от того, из какого MaterialVM был отправлен триггер, всегда срабатывала подписка Tab1 ItemTabVM.
Почему подписки Tab не остаются локальными в контексте их дочерних элементов?