扩展器控件中切换按钮的可见性无法正常工作
Visibility of toggle button in expander control is not working properly
在处理特定项目时,我使用了带有扩展器的 TypeListView 自定义控件(即 TypeListView : ListView),如下所示:
<DockPanel x:Name="MyDockPanel" Grid.Column="0" Grid.Row="1" Margin="0,0,0,0" HorizontalAlignment="Stretch" DataContext="{Binding Path=MainViewModel.TypeSelectionViewModel}">
<res:TypesListView x:Name="TypeListView"
BorderThickness="0,1,0,0"
ItemsSource="{Binding Path=Types}"
SelectedItem="{Binding Path=SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="TypeListView_SelectionChanged"
DockPanel.Dock="Left"
SelectionMode="Extended"
IsSynchronizedWithCurrentItem="True"
HorizontalAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
HorizontalContentAlignment="Stretch"
ContextMenu="{StaticResource ListViewContextMenu}"
ItemContainerStyle="{StaticResource HeaderedListViewItemStyle}"
dragDrop:DragDrop.IsDragSource="True" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ListViewItemViewModel }">
<!--<Expander HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch">-->
<Expander BorderThickness="0"
HorizontalAlignment="Stretch"
Header="{Binding}"
Style="{StaticResource ExpanderStyle}"
IsExpanded="{Binding Path=IsExpanded, Mode=TwoWay}" >
<res:TypesListView BorderThickness="0"
ItemsSource="{Binding Variants}"
ItemContainerStyle="{StaticResource ExpandedListViewItemStyle}"
dragDrop:DragDrop.IsDragSource="True"
SelectedItem="{Binding Path=TypeSelectionViewModel.SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Margin="20,0,0,0" HorizontalAlignment="Stretch"
ToolTip="{Binding Path=ConsistencyState, Converter={StaticResource ConsistencyStateToToolTipConverter}}">
<Image Margin="0,0" Source="{Binding Icon}"/>
<TextBlock Margin="3,0" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
Foreground="{Binding Path=ConsistencyState, Converter={StaticResource ConsistencyStateToColorConverter}}"/>
<Image Opacity="0.75" HorizontalAlignment="Right" Margin="0,0"
Source="{Binding Path=TypeSelectionViewModel.MainViewModel.DragDropIcon}"
ToolTip="{x:Static res:TextLibrary.TEXT_DRAGCOPYCMT}">
<Image.Visibility>
<MultiBinding Converter="{StaticResource DragDropIconVisibilityConverter}">
<Binding Path="TypeSelectionViewModel.SelectedType" />
<Binding Path="." />
</MultiBinding>
</Image.Visibility>
</Image>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</res:TypesListView>
<!--<DataTemplate >-->
<!--<DockPanel HorizontalAlignment="Stretch"
ToolTip="{Binding Path=ConsistencyState, Converter={StaticResource ConsistencyStateToToolTipConverter}}">
<Image Margin="0,0" Source="{Binding Icon}"/>
<TextBlock Margin="3,0" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
Foreground="{Binding Path=ConsistencyState, Converter={StaticResource ConsistencyStateToColorConverter}}"/>
<Image Opacity="0.75" HorizontalAlignment="Right" Margin="0,0"
Source="{Binding Path=TypeSelectionViewModel.MainViewModel.DragDropIcon}"
ToolTip="{x:Static res:TextLibrary.TEXT_DRAGCOPYCMT}">
<Image.Visibility>
<MultiBinding Converter="{StaticResource DragDropIconVisibilityConverter}">
<Binding Path="TypeSelectionViewModel.SelectedType" />
<Binding Path="." />
</MultiBinding>
</Image.Visibility>
</Image>
</DockPanel>-->
<!--</DataTemplate>-->
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</res:TypesListView>
</DockPanel>
TypeListView 的项目源是 "Types",其中 "Types" 是 listviewItemViewModel 的可观察集合。
现在,我已经使用扩展器来扩展每个 ListViewItemViewModel,当用户单击扩展按钮时,它可以再次拥有一个可观察的 listviewitemviewmodel 集合。
只是 ListViewItemViewModel 的一小段以及类型到底是什么,
只是粗略的代码:
class ListViewItemViewMOdel
{
public string Name
{
return "SomeString";
}
private ObservableCollection<ListViewItemViewModel> _variants;
public ObservableCollection<ListViewItemViewModel> Variants
{
get
{
return _variants;
}
}
}
类型选择视图模型Class:
public class TypeSelectionViewModel : INotifyPropertyChanged
{
#region Fields
private readonly MainViewModel _mainViewModel;
private readonly ObservableCollection<ListViewItemViewModel> _types = new ObservableCollection<ListViewItemViewModel>();
private List<ListViewItemViewModel> _selectedTypes = new List<ListViewItemViewModel>();
private ListViewItemViewModel _selectedType;
#endregion (Fields)
public ListViewItemViewModel SelectedType
{
get { return _selectedType; }
set
{
if (_selectedType != value)
{
//ensure that only one Item can be selected (e.g. unset selection if a variant gets selected)
if (_selectedType != null && _selectedType.IsSelected)
_selectedType.IsSelected = false;
_selectedType = value;
if (_selectedType != null)
{
_mainViewModel.TypeStructureViewModel.RootTreeViewElement.Add(
new TreeViewItemViewModel(_selectedType.AutomationObjectBase, null, _mainViewModel));
if (_mainViewModel.TypeStructureViewModel.RootTreeViewElement.Count > 1)
_mainViewModel.TypeStructureViewModel.RootTreeViewElement.RemoveAt(0);
// select root element in TreeView and expand it
_mainViewModel.TypeStructureViewModel.RootTreeViewElement[0].IsExpanded = true;
//_mainViewModel.TypeStructureViewModel.ListSelected = new List<TreeViewItemViewModel>() { _mainViewModel.TypeStructureViewModel.RootTreeViewElement[0]};
_mainViewModel.TypeStructureViewModel.RootTreeViewElement[0].IsSelected = true;
}
else
{
// clear the treeview
_mainViewModel.TypeStructureViewModel.RootTreeViewElement.Clear();
//_mainViewModel.TypeStructureViewModel.ListSelected = new List<TreeViewItemViewModel>();
}
OnPropertyChanged("SelectedType");
}
}
}
public List<ListViewItemViewModel> SelectedTypes
{
get { return _selectedTypes; }
set { _selectedTypes = value; }
}
public ObservableCollection<ListViewItemViewModel> Types
{
get { return _types; }
}
public MainViewModel MainViewModel
{
get { return _mainViewModel; }
}
public ICollectionView TypeCollectionView
{
get
{
return CollectionViewSource.GetDefaultView(Types); // adcrst2 -> moved from xaml-codebehind
}
}
#endregion (Properties)
#region Ctor
internal TypeSelectionViewModel(MainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
// initialize commands...
ExpandCollapseAllTypesCmd = new ExpandCollapseAllTypesCmd(this);
TypeCollectionView.Filter = TypeFilter;
//TODO:
IsCommandHandling = false;
}
#endregion (Ctor)
#region Methods
public void AddType(AutomationObjType automationObjType)
{
AutomationObjectBase addedType;
if (automationObjType == AutomationObjType.CMT)
{
CMTCollectionObj cmtCollectionObj = AutomationObjectFactory.AddChild(CMTContainer.Instance, AutomationObjType.CMTCollectionObj) as CMTCollectionObj;
cmtCollectionObj.AddChild(AutomationObjType.CMTemplateObj);
addedType = cmtCollectionObj.MasterCMTemplate.CMBase;
}
else if (automationObjType == AutomationObjType.AggregatedCMTemplate)
{
CMTCollectionObj cmtCollectionObj = AutomationObjectFactory.AddChild(CMTContainer.Instance, AutomationObjType.CMTCollectionObj) as CMTCollectionObj;
addedType = cmtCollectionObj.AddChild(AutomationObjType.AggregatedCMTemplate);
}
else if (automationObjType == AutomationObjType.EMT)
{
var emAggregator = AutomationObjectFactory.AddChild(EMTContainer.Instance, AutomationObjType.EMTemplateObj) as EMTemplateObj;
addedType = emAggregator.EMBase;
}
else
{
AutomationObjectBase parent;
if (automationObjType == AutomationObjType.EMT)
parent = EMTContainer.Instance;
else if (automationObjType == AutomationObjType.Function)
parent = FunctionContainer.Instance;
else
parent = EnumerationTypes.Instance;
addedType = AutomationObjectFactory.AddChild(parent, automationObjType);
}
var newType = new ListViewItemViewModel(addedType, this);
// ApplyDefaultValuesForAllAttributes and for all auto created childs
newType.AutomationObjectBase.ApplyDefaultValuesForAllAttributesRecursive();
// only Workaround:
// ADBEMI15, Michael Berenz, 01.08.2013: implemented a Save() method for Framework. moved Ivans change to this method.
// Ivan P, F37771, 15.07.2013: Save EMT after create to avoid inconsistency when TypeConfigurator
// is closed without saving (Comment Michael B.: triggered by the implementation of the sequence chain. TODO: try to find better solution)
if (newType.AutomationObjectBase is EMT ||
newType.AutomationObjectBase is AggregatedCMTemplate ||
newType.AutomationObjectBase is CMBase)
{
newType.AutomationObjectBase.Parent.Save();
newType.AutomationObjectBase.Save();
}
Types.Add(newType);
SelectedType = newType;
}
#endregion
}//end TypesOverViewViewModel
现在,我们已经为扩展器控件实现了一个控件模板。
在该控制模块中,切换按钮的可见性绑定到转换器。
转换器的目的是仅当每个 listviewitemviewModel 中至少有一个 child 时才显示切换按钮。
同样只是一个粗略的代码,用于显示转换器如何用于显示 ResouceDictionary.xaml 文件中某处扩展器中切换按钮的可见性:
<local:ExpanderVisibilityConverter x:Key="ExpanderVisibilityConverter" />
<ControlTemplate x:Key="LazyExpanderTemplate" TargetType="Expander">
<Border Name="OuterExpanderBorder"
CornerRadius="3"
BorderThickness="{TemplateBinding Border.BorderThickness}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<DockPanel>
<!--<Border Name="headerBorder" Margin="0,0,0,1" CornerRadius="2" BorderThickness="1"-->
<Border DockPanel.Dock="Top" CornerRadius="2" BorderThickness="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="True"
HorizontalAlignment="Stretch">
<Border DockPanel.Dock="Top" Name="InnerBorder"
CornerRadius="1"
BorderThickness="1">
<!--BorderBrush="{TemplateBinding BorderBrush}">-->
<Grid DockPanel.Dock="Top">
<Grid.RowDefinitions>
<RowDefinition MaxHeight="11"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Name="UpperHighlight"
Visibility="Collapsed"
Fill="#75FFFFFF"/>
<DockPanel DockPanel.Dock="Top" Name="HeaderSite"
Grid.RowSpan="2" >
<ToggleButton
DockPanel.Dock="Right"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}"
Foreground="{TemplateBinding TextElement.Foreground}"
FontFamily="{TemplateBinding TextElement.FontFamily}"
FontSize="{TemplateBinding TextElement.FontSize}"
FontStretch="{TemplateBinding TextElement.FontStretch}"
FontStyle="{TemplateBinding TextElement.FontStyle}"
FontWeight="{TemplateBinding TextElement.FontWeight}"
HorizontalContentAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding Control.VerticalContentAlignment}"
Padding="{TemplateBinding Control.Padding}"
Visibility="{Binding Path=., Converter={StaticResource ExpanderVisibilityConverter}}"
MinWidth="0"
MinHeight="0"
Margin="1">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Padding="{TemplateBinding Control.Padding}">
<Grid Background="Transparent" SnapsToDevicePixels="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="14" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Stroke="#FFA9A9A9" Name="circle" Width="14" Height="14" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Ellipse Name="shadow" Width="15" Height="15" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden" />
<!--TODO: Muss das sein-->
<Path Data="M1,1.5L4.5,5 8,1.5" Stroke="#FF666666" StrokeThickness="2" Name="arrow" HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="False" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True" >
<Setter Property="Path.Data" TargetName="arrow">
<Setter.Value>
<StreamGeometry>M1,4.5L4.5,1 8,4.5</StreamGeometry>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter Property="Shape.Stroke" TargetName="circle">
<Setter.Value>
<SolidColorBrush>#FF666666</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Shape.Stroke" TargetName="arrow">
<Setter.Value>
<SolidColorBrush>#FF222222</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="UIElement.Visibility" TargetName="shadow">
<Setter.Value>
<x:Static Member="Visibility.Visible" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
<ToggleButton.FocusVisualStyle>
<Style TargetType="IFrameworkInputElement">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Stroke="#FF000000" StrokeThickness="1" StrokeDashArray="1 2" Margin="0,0,0,0" SnapsToDevicePixels="True" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.FocusVisualStyle>
</ToggleButton>
<ContentPresenter
RecognizesAccessKey="True"
Content="{TemplateBinding HeaderedContentControl.Header}"
ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}"
ContentStringFormat="{TemplateBinding HeaderedContentControl.HeaderStringFormat}"
Margin="4,0,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
SnapsToDevicePixels="True"/>
</DockPanel>
</Grid>
</Border>
</Border>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
Name="ExpandSite"
Margin="{TemplateBinding Control.Padding}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
Visibility="Collapsed"
Focusable="False"
DockPanel.Dock="Bottom" />
</DockPanel>
</Border>
</ControlTemplate>
ExpanderVisibilityConverter 是实现 IValueConverter interface.InConvert 方法的 class Convert 方法,每个 listViewItemVIewModel 都会检查其 child 元素,如果 child,return visibility.visible 否则 visibility.collapsed
问题是,如果我在应用程序 运行 后 运行 开始填充 "Types",并且我在 "Types" 中添加了第一个 listviewItemVIewModel,ExpanderVisibilityConverter.Convert 在我检查 listviewItemViewModel 是否有 child 的地方被调用,在这种情况下它没有任何 child,因此不会显示切换按钮。
现在我向 listviewItemViewModel 添加了一个 child,现在也没有显示切换按钮,因为没有为 child 元素调用 ExpanderVisibilityConverter.Convert 方法。
现在我希望切换按钮在我将 child 添加到 listViewItemViewModel 时立即可见。
请帮我实现这个目标。
我是如何实现转换器的。
public class ExpanderVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var tvItem = value as ListViewItemViewModel;
if (tvItem != null && tvItem.Variants.Count > 0)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
我添加了实际 UI 部分的图像:
请看下图:
您必须对 ViewModel
进行一些更改。现在您的 Variants
属性 没有传播对有界项目 (ToggleButton
) 的更改。所以,
我们需要在 ObservableCollection
更改时通知绑定机制,我们将使用 CollectionChanged
事件来完成此操作。
最后我们将 ToggleButton binding
更改为 {Binding Path=Variants, Converter={StaticResource ExpanderVisibilityConverter}}
class ListViewItemViewMOdel:INotifyPropertyChanged
{
public string Name
{
return "SomeString";
}
private ObservableCollection<ListViewItemViewModel> _variants;
public ObservableCollection<ListViewItemViewModel> Variants
{
get
{
return _variants;
}
}
public ListViewItemViewMOdel()
{
_variants.CollectionChanged+=_variants_CollectionChanged;
}
void _variants_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged("Variants");
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
运行 上面的代码并告诉会发生什么?
在处理特定项目时,我使用了带有扩展器的 TypeListView 自定义控件(即 TypeListView : ListView),如下所示:
<DockPanel x:Name="MyDockPanel" Grid.Column="0" Grid.Row="1" Margin="0,0,0,0" HorizontalAlignment="Stretch" DataContext="{Binding Path=MainViewModel.TypeSelectionViewModel}">
<res:TypesListView x:Name="TypeListView"
BorderThickness="0,1,0,0"
ItemsSource="{Binding Path=Types}"
SelectedItem="{Binding Path=SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectionChanged="TypeListView_SelectionChanged"
DockPanel.Dock="Left"
SelectionMode="Extended"
IsSynchronizedWithCurrentItem="True"
HorizontalAlignment="Stretch"
ScrollViewer.HorizontalScrollBarVisibility="Auto"
ScrollViewer.VerticalScrollBarVisibility="Auto"
HorizontalContentAlignment="Stretch"
ContextMenu="{StaticResource ListViewContextMenu}"
ItemContainerStyle="{StaticResource HeaderedListViewItemStyle}"
dragDrop:DragDrop.IsDragSource="True" >
<ListView.ItemTemplate>
<DataTemplate DataType="{x:Type vm:ListViewItemViewModel }">
<!--<Expander HorizontalContentAlignment="Stretch" HorizontalAlignment="Stretch">-->
<Expander BorderThickness="0"
HorizontalAlignment="Stretch"
Header="{Binding}"
Style="{StaticResource ExpanderStyle}"
IsExpanded="{Binding Path=IsExpanded, Mode=TwoWay}" >
<res:TypesListView BorderThickness="0"
ItemsSource="{Binding Variants}"
ItemContainerStyle="{StaticResource ExpandedListViewItemStyle}"
dragDrop:DragDrop.IsDragSource="True"
SelectedItem="{Binding Path=TypeSelectionViewModel.SelectedType, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<ListView.ItemTemplate>
<DataTemplate>
<DockPanel Margin="20,0,0,0" HorizontalAlignment="Stretch"
ToolTip="{Binding Path=ConsistencyState, Converter={StaticResource ConsistencyStateToToolTipConverter}}">
<Image Margin="0,0" Source="{Binding Icon}"/>
<TextBlock Margin="3,0" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
Foreground="{Binding Path=ConsistencyState, Converter={StaticResource ConsistencyStateToColorConverter}}"/>
<Image Opacity="0.75" HorizontalAlignment="Right" Margin="0,0"
Source="{Binding Path=TypeSelectionViewModel.MainViewModel.DragDropIcon}"
ToolTip="{x:Static res:TextLibrary.TEXT_DRAGCOPYCMT}">
<Image.Visibility>
<MultiBinding Converter="{StaticResource DragDropIconVisibilityConverter}">
<Binding Path="TypeSelectionViewModel.SelectedType" />
<Binding Path="." />
</MultiBinding>
</Image.Visibility>
</Image>
</DockPanel>
</DataTemplate>
</ListView.ItemTemplate>
</res:TypesListView>
<!--<DataTemplate >-->
<!--<DockPanel HorizontalAlignment="Stretch"
ToolTip="{Binding Path=ConsistencyState, Converter={StaticResource ConsistencyStateToToolTipConverter}}">
<Image Margin="0,0" Source="{Binding Icon}"/>
<TextBlock Margin="3,0" Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}"
Foreground="{Binding Path=ConsistencyState, Converter={StaticResource ConsistencyStateToColorConverter}}"/>
<Image Opacity="0.75" HorizontalAlignment="Right" Margin="0,0"
Source="{Binding Path=TypeSelectionViewModel.MainViewModel.DragDropIcon}"
ToolTip="{x:Static res:TextLibrary.TEXT_DRAGCOPYCMT}">
<Image.Visibility>
<MultiBinding Converter="{StaticResource DragDropIconVisibilityConverter}">
<Binding Path="TypeSelectionViewModel.SelectedType" />
<Binding Path="." />
</MultiBinding>
</Image.Visibility>
</Image>
</DockPanel>-->
<!--</DataTemplate>-->
</Expander>
</DataTemplate>
</ListView.ItemTemplate>
</res:TypesListView>
</DockPanel>
TypeListView 的项目源是 "Types",其中 "Types" 是 listviewItemViewModel 的可观察集合。 现在,我已经使用扩展器来扩展每个 ListViewItemViewModel,当用户单击扩展按钮时,它可以再次拥有一个可观察的 listviewitemviewmodel 集合。
只是 ListViewItemViewModel 的一小段以及类型到底是什么, 只是粗略的代码:
class ListViewItemViewMOdel
{
public string Name
{
return "SomeString";
}
private ObservableCollection<ListViewItemViewModel> _variants;
public ObservableCollection<ListViewItemViewModel> Variants
{
get
{
return _variants;
}
}
}
类型选择视图模型Class:
public class TypeSelectionViewModel : INotifyPropertyChanged
{
#region Fields
private readonly MainViewModel _mainViewModel;
private readonly ObservableCollection<ListViewItemViewModel> _types = new ObservableCollection<ListViewItemViewModel>();
private List<ListViewItemViewModel> _selectedTypes = new List<ListViewItemViewModel>();
private ListViewItemViewModel _selectedType;
#endregion (Fields)
public ListViewItemViewModel SelectedType
{
get { return _selectedType; }
set
{
if (_selectedType != value)
{
//ensure that only one Item can be selected (e.g. unset selection if a variant gets selected)
if (_selectedType != null && _selectedType.IsSelected)
_selectedType.IsSelected = false;
_selectedType = value;
if (_selectedType != null)
{
_mainViewModel.TypeStructureViewModel.RootTreeViewElement.Add(
new TreeViewItemViewModel(_selectedType.AutomationObjectBase, null, _mainViewModel));
if (_mainViewModel.TypeStructureViewModel.RootTreeViewElement.Count > 1)
_mainViewModel.TypeStructureViewModel.RootTreeViewElement.RemoveAt(0);
// select root element in TreeView and expand it
_mainViewModel.TypeStructureViewModel.RootTreeViewElement[0].IsExpanded = true;
//_mainViewModel.TypeStructureViewModel.ListSelected = new List<TreeViewItemViewModel>() { _mainViewModel.TypeStructureViewModel.RootTreeViewElement[0]};
_mainViewModel.TypeStructureViewModel.RootTreeViewElement[0].IsSelected = true;
}
else
{
// clear the treeview
_mainViewModel.TypeStructureViewModel.RootTreeViewElement.Clear();
//_mainViewModel.TypeStructureViewModel.ListSelected = new List<TreeViewItemViewModel>();
}
OnPropertyChanged("SelectedType");
}
}
}
public List<ListViewItemViewModel> SelectedTypes
{
get { return _selectedTypes; }
set { _selectedTypes = value; }
}
public ObservableCollection<ListViewItemViewModel> Types
{
get { return _types; }
}
public MainViewModel MainViewModel
{
get { return _mainViewModel; }
}
public ICollectionView TypeCollectionView
{
get
{
return CollectionViewSource.GetDefaultView(Types); // adcrst2 -> moved from xaml-codebehind
}
}
#endregion (Properties)
#region Ctor
internal TypeSelectionViewModel(MainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
// initialize commands...
ExpandCollapseAllTypesCmd = new ExpandCollapseAllTypesCmd(this);
TypeCollectionView.Filter = TypeFilter;
//TODO:
IsCommandHandling = false;
}
#endregion (Ctor)
#region Methods
public void AddType(AutomationObjType automationObjType)
{
AutomationObjectBase addedType;
if (automationObjType == AutomationObjType.CMT)
{
CMTCollectionObj cmtCollectionObj = AutomationObjectFactory.AddChild(CMTContainer.Instance, AutomationObjType.CMTCollectionObj) as CMTCollectionObj;
cmtCollectionObj.AddChild(AutomationObjType.CMTemplateObj);
addedType = cmtCollectionObj.MasterCMTemplate.CMBase;
}
else if (automationObjType == AutomationObjType.AggregatedCMTemplate)
{
CMTCollectionObj cmtCollectionObj = AutomationObjectFactory.AddChild(CMTContainer.Instance, AutomationObjType.CMTCollectionObj) as CMTCollectionObj;
addedType = cmtCollectionObj.AddChild(AutomationObjType.AggregatedCMTemplate);
}
else if (automationObjType == AutomationObjType.EMT)
{
var emAggregator = AutomationObjectFactory.AddChild(EMTContainer.Instance, AutomationObjType.EMTemplateObj) as EMTemplateObj;
addedType = emAggregator.EMBase;
}
else
{
AutomationObjectBase parent;
if (automationObjType == AutomationObjType.EMT)
parent = EMTContainer.Instance;
else if (automationObjType == AutomationObjType.Function)
parent = FunctionContainer.Instance;
else
parent = EnumerationTypes.Instance;
addedType = AutomationObjectFactory.AddChild(parent, automationObjType);
}
var newType = new ListViewItemViewModel(addedType, this);
// ApplyDefaultValuesForAllAttributes and for all auto created childs
newType.AutomationObjectBase.ApplyDefaultValuesForAllAttributesRecursive();
// only Workaround:
// ADBEMI15, Michael Berenz, 01.08.2013: implemented a Save() method for Framework. moved Ivans change to this method.
// Ivan P, F37771, 15.07.2013: Save EMT after create to avoid inconsistency when TypeConfigurator
// is closed without saving (Comment Michael B.: triggered by the implementation of the sequence chain. TODO: try to find better solution)
if (newType.AutomationObjectBase is EMT ||
newType.AutomationObjectBase is AggregatedCMTemplate ||
newType.AutomationObjectBase is CMBase)
{
newType.AutomationObjectBase.Parent.Save();
newType.AutomationObjectBase.Save();
}
Types.Add(newType);
SelectedType = newType;
}
#endregion
}//end TypesOverViewViewModel
现在,我们已经为扩展器控件实现了一个控件模板。 在该控制模块中,切换按钮的可见性绑定到转换器。 转换器的目的是仅当每个 listviewitemviewModel 中至少有一个 child 时才显示切换按钮。
同样只是一个粗略的代码,用于显示转换器如何用于显示 ResouceDictionary.xaml 文件中某处扩展器中切换按钮的可见性:
<local:ExpanderVisibilityConverter x:Key="ExpanderVisibilityConverter" />
<ControlTemplate x:Key="LazyExpanderTemplate" TargetType="Expander">
<Border Name="OuterExpanderBorder"
CornerRadius="3"
BorderThickness="{TemplateBinding Border.BorderThickness}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
SnapsToDevicePixels="True">
<DockPanel>
<!--<Border Name="headerBorder" Margin="0,0,0,1" CornerRadius="2" BorderThickness="1"-->
<Border DockPanel.Dock="Top" CornerRadius="2" BorderThickness="1"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="True"
HorizontalAlignment="Stretch">
<Border DockPanel.Dock="Top" Name="InnerBorder"
CornerRadius="1"
BorderThickness="1">
<!--BorderBrush="{TemplateBinding BorderBrush}">-->
<Grid DockPanel.Dock="Top">
<Grid.RowDefinitions>
<RowDefinition MaxHeight="11"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Rectangle Name="UpperHighlight"
Visibility="Collapsed"
Fill="#75FFFFFF"/>
<DockPanel DockPanel.Dock="Top" Name="HeaderSite"
Grid.RowSpan="2" >
<ToggleButton
DockPanel.Dock="Right"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsExpanded}"
Foreground="{TemplateBinding TextElement.Foreground}"
FontFamily="{TemplateBinding TextElement.FontFamily}"
FontSize="{TemplateBinding TextElement.FontSize}"
FontStretch="{TemplateBinding TextElement.FontStretch}"
FontStyle="{TemplateBinding TextElement.FontStyle}"
FontWeight="{TemplateBinding TextElement.FontWeight}"
HorizontalContentAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalContentAlignment="{TemplateBinding Control.VerticalContentAlignment}"
Padding="{TemplateBinding Control.Padding}"
Visibility="{Binding Path=., Converter={StaticResource ExpanderVisibilityConverter}}"
MinWidth="0"
MinHeight="0"
Margin="1">
<ToggleButton.Style>
<Style TargetType="ToggleButton">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border Padding="{TemplateBinding Control.Padding}">
<Grid Background="Transparent" SnapsToDevicePixels="False">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="14" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Ellipse Stroke="#FFA9A9A9" Name="circle" Width="14" Height="14" HorizontalAlignment="Center" VerticalAlignment="Center" />
<Ellipse Name="shadow" Width="15" Height="15" HorizontalAlignment="Center" VerticalAlignment="Center" Visibility="Hidden" />
<!--TODO: Muss das sein-->
<Path Data="M1,1.5L4.5,5 8,1.5" Stroke="#FF666666" StrokeThickness="2" Name="arrow" HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="False" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True" >
<Setter Property="Path.Data" TargetName="arrow">
<Setter.Value>
<StreamGeometry>M1,4.5L4.5,1 8,4.5</StreamGeometry>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter Property="Shape.Stroke" TargetName="circle">
<Setter.Value>
<SolidColorBrush>#FF666666</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="Shape.Stroke" TargetName="arrow">
<Setter.Value>
<SolidColorBrush>#FF222222</SolidColorBrush>
</Setter.Value>
</Setter>
<Setter Property="UIElement.Visibility" TargetName="shadow">
<Setter.Value>
<x:Static Member="Visibility.Visible" />
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.Style>
<ToggleButton.FocusVisualStyle>
<Style TargetType="IFrameworkInputElement">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Border>
<Rectangle Stroke="#FF000000" StrokeThickness="1" StrokeDashArray="1 2" Margin="0,0,0,0" SnapsToDevicePixels="True" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ToggleButton.FocusVisualStyle>
</ToggleButton>
<ContentPresenter
RecognizesAccessKey="True"
Content="{TemplateBinding HeaderedContentControl.Header}"
ContentTemplate="{TemplateBinding HeaderedContentControl.HeaderTemplate}"
ContentStringFormat="{TemplateBinding HeaderedContentControl.HeaderStringFormat}"
Margin="4,0,0,0"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
SnapsToDevicePixels="True"/>
</DockPanel>
</Grid>
</Border>
</Border>
<ContentPresenter Content="{TemplateBinding ContentControl.Content}"
ContentTemplate="{TemplateBinding ContentControl.ContentTemplate}"
ContentStringFormat="{TemplateBinding ContentControl.ContentStringFormat}"
Name="ExpandSite"
Margin="{TemplateBinding Control.Padding}"
HorizontalAlignment="{TemplateBinding Control.HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding Control.VerticalContentAlignment}"
Visibility="Collapsed"
Focusable="False"
DockPanel.Dock="Bottom" />
</DockPanel>
</Border>
</ControlTemplate>
ExpanderVisibilityConverter 是实现 IValueConverter interface.InConvert 方法的 class Convert 方法,每个 listViewItemVIewModel 都会检查其 child 元素,如果 child,return visibility.visible 否则 visibility.collapsed
问题是,如果我在应用程序 运行 后 运行 开始填充 "Types",并且我在 "Types" 中添加了第一个 listviewItemVIewModel,ExpanderVisibilityConverter.Convert 在我检查 listviewItemViewModel 是否有 child 的地方被调用,在这种情况下它没有任何 child,因此不会显示切换按钮。 现在我向 listviewItemViewModel 添加了一个 child,现在也没有显示切换按钮,因为没有为 child 元素调用 ExpanderVisibilityConverter.Convert 方法。
现在我希望切换按钮在我将 child 添加到 listViewItemViewModel 时立即可见。 请帮我实现这个目标。
我是如何实现转换器的。
public class ExpanderVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var tvItem = value as ListViewItemViewModel;
if (tvItem != null && tvItem.Variants.Count > 0)
{
return Visibility.Visible;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return Binding.DoNothing;
}
}
我添加了实际 UI 部分的图像:
请看下图:
您必须对 ViewModel
进行一些更改。现在您的 Variants
属性 没有传播对有界项目 (ToggleButton
) 的更改。所以,
我们需要在 ObservableCollection
更改时通知绑定机制,我们将使用 CollectionChanged
事件来完成此操作。
最后我们将 ToggleButton binding
更改为 {Binding Path=Variants, Converter={StaticResource ExpanderVisibilityConverter}}
class ListViewItemViewMOdel:INotifyPropertyChanged
{
public string Name
{
return "SomeString";
}
private ObservableCollection<ListViewItemViewModel> _variants;
public ObservableCollection<ListViewItemViewModel> Variants
{
get
{
return _variants;
}
}
public ListViewItemViewMOdel()
{
_variants.CollectionChanged+=_variants_CollectionChanged;
}
void _variants_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
OnPropertyChanged("Variants");
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string prop)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
运行 上面的代码并告诉会发生什么?