我仍然在抓住这个问题的更好表述。这就是我到目前为止所做的:
包含模块列表的自定义类,其中一些模块是通用模块的专用版本
[Serializable]
class ModuleList
{
public ObservableCollection<Module> Items
{
get;
set;
}
public ModuleList()
{
Items = new ObservableCollection<Module>();
}
}
[Serializable]
class Module : INotifyPropertyChanged
{
private string name;
public string Name
{
get
{
return this.name;
}
set
{
if(this.name != value)
{
this.name = value;
this.NotifyPropertyChanged("Name");
}
}
}
[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(string propName)
{
if(this.PropertyChanged != null)
{
this.PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
}
[Serializable]
class SpecializedModule : Module
{
public SpecializedModule()
{
mode = false;
}
private bool mode;
public bool Mode
{
get
{
return this.mode;
}
set
{
if(this.mode != value)
{
this.mode = value;
this.NotifyPropertyChanged("Mode");
}
}
}
}
项目列表用作ItemSource
的{{1}}。选择列表中的项目将使用ListView
加载相应的.xaml。 XamlReader
用作模块特定UI的容器。通过将网格的数据上下文设置为模块列表
System.Windows.Control.Grid
然后使用转换器将子网格的switch (it.ModID)
{
case "SOMEMODID":
loadGrid("somemodule.xaml");
break;
default:
loadGrid("_dummy.xaml");
break;
}
grpModCfg.DataContext = modListSel;
设置为相应的模块
DataContext
一个例子.xaml:
public class ModuleListToModuleConverter : System.Windows.Data.IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value is ModuleList)
{
ModuleList modList = value as ModuleList;
int idx = modList.getItemIndex(parameter as string);
if (idx != -1)
{
return modList.Items[idx];
}
else
{
return null;
}
}
else
{
return null;
}
}
public object ConvertBack(object obj, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
这适用于绑定特定模块的属性。但是,我也依赖于其他模块的属性,所以我需要像
这样的东西到目前为止我尝试了什么:
<Grid Name="grdSomeMod"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ModCfg;assembly=ModCfg"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="395"
d:DesignWidth="480" >
<Grid.Resources>
<local:ModuleListToModuleConverter x:Key="modlistConverter" />
</Grid.Resources>
<Grid.DataContext>
<Binding Converter="{StaticResource modlistConverter}" ConverterParameter="SOMEMODEID" />
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="240*" />
<ColumnDefinition Width="240*" />
</Grid.ColumnDefinitions>
<CheckBox Content="Some Parameter" Height="16" HorizontalAlignment="Left" IsChecked="{Binding PropertyOfSomeModule}" Margin="6,6,0,0" VerticalAlignment="Top" Name="chkSomePar" />
</Grid>
设置保留到我的模块列表中,以便我可以访问所有这些DataContext
绑定到moduleA的属性并将IsEnabled
绑定到模块B的属性是不可能的。< / LI>
方法A的XAML:
IsChecked
方法B的XAML:
<Grid Name="grdSomeMod"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ModCfg;assembly=ModCfg"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="395"
d:DesignWidth="480" >
<Grid.Resources>
<local:ModuleListToModuleConverter x:Key="modlistConverter" />
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="240*" />
<ColumnDefinition Width="240*" />
</Grid.ColumnDefinitions>
<CheckBox Content="Some Parameter" Height="16" HorizontalAlignment="Left" IsChecked="{Binding PropertyOfSomeModule}" Margin="6,6,0,0" VerticalAlignment="Top" Name="chkSomePar>
<CheckBox.DataContext>
<Binding Converter="{StaticResource modlistConverter}" ConverterParameter="SOMEMODEID" />
</Checkbox.DataContext>
</CheckBox>
</Grid>
接近B的转换器:
<Grid Name="grdSomeMod"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ModCfg;assembly=ModCfg"
mc:Ignorable="d"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
d:DesignHeight="395"
d:DesignWidth="480" >
<Grid.Resources>
<local:ModuleListToModuleConverter x:Key="modlistConverter" />
<local:ModuleListToModuleDefineConverter x:Key="defBinaryConverter" />
</Grid.Resources>
<Grid.DataContext>
<Binding Converter="{StaticResource modlistConverter}" ConverterParameter="SOMEMODEID" />
</Grid.DataContext>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="240*" />
<ColumnDefinition Width="240*" />
</Grid.ColumnDefinitions>
<CheckBox Content="Some Parameter" Height="16" HorizontalAlignment="Left" IsChecked="{Binding PropertyOfSomeModule}" Margin="6,6,0,0" VerticalAlignment="Top" Name="chkSomePar">
<CheckBox.IsChecked>
<!--not working atm-->
<Binding Path="." Converter="{StaticResource defBinaryConverter}" ConverterParameter="SOMEMODID,SomeProperty" />
</CheckBox.IsChecked>
<CheckBox.IsEnabled>
<Binding Converter="{StaticResource defBinaryConverter}" ConverterParameter="SOMEOTHERMODID,SomeOtherProperty" />
</CheckBox.IsEnabled>
</CheckBox>
</Grid>
这是相当多的代码,但这也是我一直试图解决一个星期的问题,我觉得我变得非常接近,但可能会忽略一些东西。
也许有更优雅的方式来做这件事?
答案 0 :(得分:0)
我意识到即使我设法按照我想要的方式在XAML中映射我的依赖项,我也无法将它们传递给C#代码。例如,即使在GUI中禁用了某个功能,我也无法在我的模块对象中知道它。所以我的解决方案是为那些具有依赖项的属性添加一个额外的属性,将该属性映射到“IsEnabled”,并在加载XAML后更新对象内的值。使用我在问题中发布的片段,对我的改变是
[Serializable]
class SpecializedModule : Module
{
public SpecializedModule()
{
mode = false;
}
private bool mode;
public bool Mode
{
get
{
return this.mode;
}
set
{
if(this.mode != value)
{
this.mode = value;
this.NotifyPropertyChanged("Mode");
}
}
private bool modeEn;
public bool ModeEn
{
get
{
return this.modeEn;
}
set
{
if(this.modeEn != value)
{
this.modeEn = value;
this.NotifyPropertyChanged("ModeEn");
}
}
}
}
单个XAML的数据上下文现在设置为相应的模块,因此绑定很简单。
<CheckBox Content="Some Parameter" Height="16" HorizontalAlignment="Left" IsChecked="{Binding PropertyOfSomeModule}" Margin="6,6,0,0" VerticalAlignment="Top" Name="chkSomePar" IsChecked="{Binding Mode}" IsEnabled="{Binding ModeEn}" />
现在我所要做的就是编写一个函数,检查我的功能所依赖的模块是否在列表中,并在用户选择项目时调用它。
switch (it.ModID)
{
case "SOMEMODID":
loadGrid("somemodule.xaml");
break;
default:
loadGrid("_dummy.xaml");
break;
}
grpModCfg.DataContext = modListSel;
modList.refreshDependencies();
刷新功能看起来像
public void refreshDependencies()
{
foreach (Module mod in this.Items)
{
int idx = -1;
switch (mod.ModID)
{
case "SOMEMODID":
// Mode depends on SOMEOTHERMODID
idx = this.modList.getItemIndex("SOMEOTHERMODID");
if (idx != -1)
{
(mod as SomeModule).ModeEn = (this.Items[idx] as SomeOtherModule).SomeOtherProperty;
}
else
{
(mod as SomeModule).ModeEn = false;
}
break;
}
}
}
所以现在我可以访问代码中的所有模块,GUI反映了对象的状态。
修改强>
我也意识到在代码之外编辑单个XAML文件没有任何意义,因为无论如何C#代码都必须改变,所以我遵循了Peter Duniho的建议并切换到加载单个XAML文件作为资源。 This MSDN page帮助了我,因此现在代码的加载部分缩减为
loadGrid(it.ModID);
grpModCfg.DataContext = it;
modList.refreshDependencies();