XAML:
<StackPanel Name="StackPanel1"> <StackPanel.Resources> <local:ExpanderToBooleanConverter x:Key="ExpanderToBooleanConverter" /> </StackPanel.Resources> <Expander Header="Expander 1" IsExpanded="{Binding SelectedExpander,Mode=TwoWay,Converter={StaticResource ExpanderToBooleanConverter},ConverterParameter=1}"> <TextBlock>Expander 1</TextBlock> </Expander> <Expander Header="Expander 2" IsExpanded="{Binding SelectedExpander,ConverterParameter=2}"> <TextBlock>Expander 2</TextBlock> </Expander> <Expander Header="Expander 3" IsExpanded="{Binding SelectedExpander,ConverterParameter=3}"> <TextBlock>Expander 3</TextBlock> </Expander> <Expander Header="Expander 4" IsExpanded="{Binding SelectedExpander,ConverterParameter=4}"> <TextBlock>Expander 4</TextBlock> </Expander> </StackPanel>
转换器:
public class ExpanderToBooleanConverter : IValueConverter { public object Convert(object value,Type targetType,object parameter,CultureInfo culture) { return (value == parameter); // I tried thoses too : return value != null && (value.ToString() == parameter.ToString()); return value != null && (value.ToString().Equals(parameter.ToString())); } public object ConvertBack(object value,CultureInfo culture) { return System.Convert.ToBoolean(value) ? parameter : null; } }
视图模型:
public class ExpanderListviewmodel : INotifyPropertyChanged { private Object _selectedExpander; public Object SelectedExpander { get { return _selectedExpander; } set { if (_selectedExpander == value) { return; } _selectedExpander = value; OnPropertyChanged("SelectedExpander"); } } public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { PropertyChangedEventHandler handler = PropertyChanged; if (handler != null) { handler(this,new PropertyChangedEventArgs(propertyName)); } } }
初始化
var viewmodel = new ExpanderListviewmodel(); StackPanel1.DataContext = viewmodel; viewmodel.SelectedExpander = 1; // I tried this also viewmodel.SelectedExpander = "1";
它工作正常,但现在我想在应用程序启动时扩展其中一个扩展器!
我已经尝试将值(1,2或3)放在SelectedExpander属性中,但默认情况下没有扩展器扩展!
如何将这种可能性添加到我的扩展器中?
解决方法
>为Expander 2调用ConvertBack及其当前的IsExpanded值(false),并返回null.
> SelectedExpander更新为null.
>为所有其他扩展器调用Convert,因为SelectedExpander已更改,导致所有其他IsExpanded值也设置为false.
当然,这不是正确的行为.所以解决方案依赖于源永远不会被更新,除非用户实际切换扩展器.
因此,我怀疑问题是控件的初始化以某种方式触发源更新.即使扩展器1被正确初始化为扩展,当在任何其他扩展器上刷新绑定时,它也会被重置.
要使ConvertBack正确,它需要知道其他扩展器:如果所有扩展器都折叠,它应该只返回null.但是,我没有看到从转换器中处理这个问题的干净方法.也许最好的解决方案是使用单向绑定(无ConvertBack)并以这种方式或类似方式处理Expanded和Collapsed事件(其中_expanders是所有扩展器控件的列表):
private void OnExpanderIsExpandedChanged(object sender,RoutedEventArgs e) { var selectedExpander = _expanders.FirstOrDefault(e => e.IsExpanded); if (selectedExpander == null) { viewmodel.SelectedExpander = null; } else { viewmodel.SelectedExpander = selectedExpander.Tag; } }
在这种情况下,我使用Tag作为viewmodel中使用的标识符.
编辑:
要以更“MVVM”的方式解决它,您可以为每个扩展器提供一组视图模型,并使用单独的属性将IsExpanded绑定到:
public class Expanderviewmodel { public bool IsSelected { get; set; } // todo INotifyPropertyChanged etc. }
将集合存储在ExpanderListviewmodel中,并在初始化时为每个集合添加PropertyChanged处理程序:
// in ExpanderListviewmodel foreach (var expanderviewmodel in Expanders) { expanderviewmodel.PropertyChanged += Expander_PropertyChanged; } ... private void Expander_PropertyChanged(object sender,PropertyChangedEventArgs e) { var thisExpander = (Expanderviewmodel)sender; if (e.PropertyName == "IsSelected") { if (thisExpander.IsSelected) { foreach (var otherExpander in Expanders.Except(new[] {thisExpander})) { otherExpander.IsSelected = false; } } } }
然后将每个扩展器绑定到Expanders集合的不同项:
<Expander Header="Expander 1" IsExpanded="{Binding Expanders[0].IsSelected}"> <TextBlock>Expander 1</TextBlock> </Expander> <Expander Header="Expander 2" IsExpanded="{Binding Expanders[1].IsSelected}"> <TextBlock>Expander 2</TextBlock> </Expander>
(您可能还需要研究定义自定义ItemsControl以根据集合动态生成扩展器.)
在这种情况下,将不再需要SelectedExpander属性,但可以通过以下方式实现:
private Expanderviewmodel _selectedExpander; public Expanderviewmodel SelectedExpander { get { return _selectedExpander; } set { if (_selectedExpander == value) { return; } // deselect old expander if (_selectedExpander != null) { _selectedExpander.IsSelected = false; } _selectedExpander = value; // select new expander if (_selectedExpander != null) { _selectedExpander.IsSelected = true; } OnPropertyChanged("SelectedExpander"); } }
并将以上PropertyChanged处理程序更新为:
if (thisExpander.IsSelected) { ... SelectedExpander = thisExpander; } else { SelectedExpander = null; }
所以现在这两行是初始化第一个扩展器的等效方法:
viewmodel.SelectedExpander = viewmodel.Expanders[0]; viewmodel.Expanders[0].IsSelected = true;