2016-01-20 41 views
0

我仍然在为更好地表达问题而努力。这是我到目前为止有:绑定两个动态加载的xaml文件的两个UI元素

包含的模块列表,其中一些模块是通用模块

[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"); 
     } 
     } 
    } 
} 

的项目清单作为ItemSourceListView的专门版本的自定义类。选择列表中的项目将使用XamlReader加载相应的.xaml。 A System.Windows.Control.Grid,被用作模块特定UI的容器。如预期由网格的数据上下文设置为模块列表

switch (it.ModID) 
{ 
    case "SOMEMODID": 
     loadGrid("somemodule.xaml"); 
     break; 
    default: 
     loadGrid("_dummy.xaml"); 
     break; 
} 
grpModCfg.DataContext = modListSel; 

然后使用转换器

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; 
     } 
    } 

一个例子的子网格的DataContext设定到相应的模块“正常”结合的作品。 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 ==真/假

我试了一下,到目前为止:

  • 方法的一个
    • 离开DataContext设置为我的模块列表,以便我可以访问所有这些
    • 设置每个元素的情况下,让那些需要访问另一个模块具有上下文
    • 问题:一旦我设定上下文到一个模块,我无法访问另一个模块,因此将IsEnabled绑定到moduleA的属性并将IsChecked绑定到模块B的属性是不可能的。
  • 方法乙
    • 使用的转换器瓦特/参数指定模块和哪个属性以搜索
    • 问题(逗号分隔,然后在变换器分割):它适用于的IsEnabled,但器isChecked抱怨关于需要一个路径,因为双向绑定,我似乎无法找到一个工程。

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> 

XAML的方法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> 

而对于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; 
    } 
} 

这是一个相当批量的代码,但这也是我一直试图解决大约一周的问题,而且我觉得我正在变得非常接近,但是可能会忽略某些东西。

也许有一个更优雅的方式来做到这一点?

+0

我觉得这个问题不清楚。例如。为什么要动态加载XAML?为什么不根据需要将适当的模板作为参考资源?为什么数据上下文不仅仅是模板加载/引用的模块实例?上面有一个很大的代码转储,但它不完整,同时包含了对于这个问题看起来并不是必须的东西。请阅读[mcve]了解如何提供一个很好的代码示例。请具体说明代码的作用,以及与您想要的不同之处。 –

+0

@PeterDuniho我应该可能已经说过我对WPF来说也是一个新手,所以我的问题可能仅仅是因为我对这个问题缺乏了解。动态加载XAML似乎是没有一个巨大的XAML与其中的所有模块的最佳方式。我没有看到如何从资源加载页面与加载文件不同。如果每个模块的数据上下文从一开始就被设置为模块,就无法访​​问另一个模块的属性来映射依赖关系,据我所知 - 这是我的问题。 – Soukyuu

+0

_“我没有看到如何从资源中加载页面与在我的情况下加载文件不同”_ - 如果您已将模板定义为资源,那么您可以利用WPF中的现有模板功能,而不是必须编写代码隐藏来匹配具有数据类型的模板。至于其他问题,不幸的是,您的问题并未明确“访问另一个模块的属性来映射依赖关系”的含义。模板只需要访问它定义的模块类型的属性。 –

回答

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#代码将不得不反正变了,所以我也跟着彼得的建议Duniho并切换到加载单个XAML文件作为资源。 This MSDN page在这个过程中对我有所帮助,所以现在把代码的加载部分缩减为

loadGrid(it.ModID); 
grpModCfg.DataContext = it; 
modList.refreshDependencies();