【发布时间】:2016-01-20 14:32:05
【问题描述】:
我仍在努力寻找更好的问题表述。这是我目前所拥有的:
包含模块列表的自定义类,其中一些模块是通用模块的专用版本
[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 用于ListView。选择列表中的项目使用XamlReader 加载相应的.xaml。 System.Windows.Control.Grid,用作特定于模块的 UI 的容器。通过将网格的数据上下文设置为模块列表,“正常”绑定按预期工作
switch (it.ModID)
{
case "SOMEMODID":
loadGrid("somemodule.xaml");
break;
default:
loadGrid("_dummy.xaml");
break;
}
grpModCfg.DataContext = modListSel;
然后使用转换器将子网格的DataContext 设置为相应的模块
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;
}
}
.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.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>
这适用于绑定特定模块的属性。但是,例如,我也依赖于其他模块的属性,所以我需要类似
- CheckBoxA.IsChecked = moduleA.SomeProperty
- CheckBoxB.IsEnabled = moduleB 在列表中 && moduleB.SomeOtherProperty == true/false
到目前为止我尝试了什么:
- 方法 A
- 将
DataContext设置保留到我的模块列表中,以便我可以访问所有这些 - 为每个元素设置上下文,以便那些需要访问另一个模块的人拥有上下文
- 问题:一旦我将上下文设置到一个模块,我就无法访问另一个模块,因此将
IsEnabled绑定到模块A的属性和IsChecked绑定到模块B的属性是不可能的。
- 将
- 方法 B
- 使用带参数的转换器指定要搜索的模块和属性(逗号分隔,然后在转换器中拆分)
- 问题:它适用于 IsEnabled,但 IsChecked 抱怨由于双向绑定需要路径,我似乎无法找到一个有效的路径。
方法 A 的 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 的 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" />
<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>
以及方法 B 的转换器:
public class ModuleListToModuleDefineConverter : 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;
string paramList = parameter as string;
string[] pars = paramList.Split(',');
int idx = modlist.getItemIndex(pars[0]);
if (idx != -1)
{
switch (pars[0])
{
case "SOMEMODID":
switch (pars[1])
{
case "SomeProperty":
return (modlist.Items[idx] as SomeModule).C0x10;
}
break;
case "SOMEOTHERMODID":
switch (pars[1])
{
case "SomeOtherProperty":
return (modlist.Items[idx] as SomeOtherMod).SomeOtherProperty;
}
break;
}
}
return false;
}
else
{
return false;
}
}
public object ConvertBack(object obj, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
return null;
}
}
这是相当多的代码,但这也是我已经尝试解决了大约一周的问题,我觉得我已经非常接近了,但可能会忽略一些东西。
也许有更优雅的方式来做到这一点?
【问题讨论】:
-
我觉得这个问题不清楚。例如。为什么要动态加载 XAML?为什么不将适当的模板作为您根据需要引用的资源?为什么数据上下文不仅仅是加载/引用模板的模块实例?上面有一个很大的代码转储,但它不完整,同时包含了似乎对这个问题不是绝对必要的东西。请阅读minimal reproducible example 以获取有关如何提供良好代码示例的建议。请具体说明代码的作用以及与您想要的有何不同。
-
@PeterDuniho 我应该说我对 WPF 也很陌生,所以我的问题可能仅仅是因为我对此事缺乏了解。动态加载 XAML 似乎是没有一个包含所有模块的大型 XAML 的最佳方法。在我的情况下,我看不出从资源加载页面与从文件加载有何不同。如果每个模块的数据上下文从一开始就设置为模块,那么据我所知,无法访问另一个模块的属性来映射依赖关系 - 这是我的问题。
-
“在我的情况下,我看不出从资源加载页面与从文件加载有何不同” -- 如果您已将模板定义为资源,那么您可以利用 WPF 中现有的模板功能,而不必编写代码隐藏来匹配模板与数据类型。至于其余的,不幸的是,您的问题并不清楚“访问另一个模块的属性以映射依赖项”是什么意思。模板应该只需要访问为其定义的模块类型的属性。
-
@PeterDuniho:感谢有关资源的想法,它大大简化了我的加载。我还意识到我犯了一些其他的逻辑错误,所以我设法想出了一个解决方案。抱歉问题表述不清楚,下次我会尽量说清楚。
标签: c# wpf data-binding