绑定两个动态加载的xaml文件的两个UI元素

时间:2016-01-20 14:32:05

标签: c# wpf data-binding

我仍然在抓住这个问题的更好表述。这就是我到目前为止所做的:

包含模块列表的自定义类,其中一些模块是通用模块的专用版本

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

这适用于绑定特定模块的属性。但是,我也依赖于其他模块的属性,所以我需要像

这样的东西
  • CheckBoxA.IsChecked = moduleA.SomeProperty
  • CheckBoxB.IsEnabled = moduleB在列表中&amp;&amp; moduleB.SomeOtherProperty == true / false

到目前为止我尝试了什么:

  • 接近A.
    • <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>
  • 接近B.
    • 使用转换器w /参数指定要搜索的模块和属性(以逗号分隔,然后在转换器中拆分)
    • 问题:它适用于IsEnabled,但IsChecked因为双向绑定而抱怨需要一个Path,而我似乎无法找到一个有效的。

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

这是相当多的代码,但这也是我一直试图解决一个星期的问题,我觉得我变得非常接近,但可能会忽略一些东西。

也许有更优雅的方式来做这件事?

1 个答案:

答案 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();