【问题标题】:Multiple bindings to show dynamic content显示动态内容的多个绑定
【发布时间】:2018-07-09 10:00:53
【问题描述】:

在创建包含动态内容的布局时,我经常这样做:

<Grid Visibility="{Binding IsLastSelectedItem, Converter=...}" >
    <Grid Visibility="{Binding IsStatisticAvailable, Converter=...}" >
        <TextBlock Visibility="{Binding HasStatistic, Converter=...}"
                   Text="{Binding Statistic}" />
    </Grid>
</Grid>

这里的 2 个容器仅用于显示基于多个条件的内容,它是 3 个绑定与逻辑 AND 结合。

使用 MVVM 可以创建单个属性并直接绑定到它:

public bool ShowStatistic => IsLastSelectedItem && IsStatisticAvailable && HasStatistic;

但这并不总是可能的/容易的,并且有缺点。我必须监视所有 conditional 属性的变化并为结果属性发出通知。如果条件属性之一是静态的或特定于视图的,那么添加事件处理程序、订阅/取消订阅等以使其在视图模型和/或上升通知中可用是不可避免的麻烦。

昨天在 SO 的帮助下,我创建了 nice control 来添加动态内容。它有一个 bool 依赖属性来显示/隐藏其内容。现在我正在考虑如何避免为多个绑定嵌套多个此类控件,如上例所示。

问题:管理用于创建具有动态内容的布局的多重绑定最佳(可重复使用、易于使用、简短、清晰)方法是什么?我可能缺乏合适的词来找到类似的问题。


我可以想到多重绑定和转换器。可重复使用的?一定不行。还是不行?

我可以考虑创建具有多个 bool 属性的自定义 容器 (MyGrid),由多个绑定和其他一些属性用于指定表达式:ANDOR 等.

也许我遗漏了一些明显而简单的东西?

【问题讨论】:

    标签: c# wpf layout mvvm binding


    【解决方案1】:

    这是使用附加属性的解决方案:

    public static class Logic
    {
        public enum Equation { Empty, AandBorCandD, ... }; // more options
    
        public static bool GetA(DependencyObject obj) => (bool)obj.GetValue(AProperty);
        public static void SetA(DependencyObject obj, bool value) => obj.SetValue(AProperty, value);
        public static readonly DependencyProperty AProperty =
            DependencyProperty.RegisterAttached("A", typeof(bool), typeof(Logic), new PropertyMetadata(OnValueChanged));
    
        // reduced content, normal attached properties, defined similar to AProperty above
        public static bool GetB... // BProperty
        public static bool GetC... // CProperty
        public static bool GetD... // DProperty
        public static Equation GetEquation... // EquationProperty
        public static bool GetR... // RProperty = result
    
        static void OnValueChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            switch (GetEquation(obj))
            {
                case Equation.AandBorCandD:
                    SetR(obj, GetA(obj) && GetB(obj) || GetC(obj) && GetD(obj));
                    break;
                ... // other options
            }
        }
    

    这个想法是使用几个附加属性进行绑定,并将需要的属性绑定到“结果”,每次更改时都会重新计算(类似于多重绑定)。

    公式指定为enum,有switch/case计算结果。

    使用简单:

    <TextBlock local:Logic.A="{Binding ...}"
               local:Logic.B="{Binding ...}"
               local:Logic.C="{Binding ...}"
               local:Logic.D="{Binding ...}"
               local:Logic.Equation="AandBorCandD"
               Visibility="{Binding (local:Logic.R), RelativeSource={RelativeSource Self}, Converter=...}" />
    

    注意事项:

    • 作为绑定源的附加属性需要() 围绕路径。
    • 此解决方案只能用于每个依赖对象的单个绑定。

    【讨论】:

      【解决方案2】:

      在这种情况下,多值转换器是理想的。

      类似于以下内容:

      public class MultiBoolToVisibilityConverter : IMultiValueConverter
      {
          public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
          {
              if(values.All(v=>v is bool))
                  return values.All(v=>(bool)v)?
                      Visibility.Visible:
                      Visibility.Hidden;
              else
                  throw new ArgumentException("Cannot determine boolean state of non-boolean value");
          }
      }
      

      这样你就有了一个可扩展的转换器,它接受一个或多个布尔值并仅在“values”数组中的所有项目都为真时返回“Visible”。

      在您的 xaml 中:

      <TextBlock Text="{Binding Statistic}" >
          <TextBlock.Visibility>
              <MultiBinding Converter="{StaticResource MultiBoolToVisibilityConverter }">
                  <Binding Path="IsLastSelectedItem" />
                  <Binding Path="IsStatisticAvailable" />
                  <Binding Path="HasStatistic" />
              </MultiBinding>
          </TextBlock.Visibility>
      </TextBlock>
      

      在您有多个标志来确定可见性的任何区域中高度可重用,而且它也是可单元测试的。

      【讨论】:

      • values.All() 等于 ANDOR 怎么样?我可以在内部创建许多具有不同名称和逻辑的转换器,并使用我需要的一个。我不确定如何反映哪个绑定是什么条件。例如。如果我调用转换器AllTrueAnyTrue 很清楚,但如果我想要条件A AND B OR C AND D(其中a、b、c、d 是绑定)?使用第一个绑定作为参数(字符串?)转换器以伪形式指定条件?嗯..
      • 我无法理解可能有用的场景,但这是可能的。它将一定程度的逻辑放入 xaml(在值绑定之间指定伪代码操作数),这使得代码维护更加困难。对于您所建议的更复杂的解决方案,最好在您的 ViewModel 中添加标志。
      • 最坏的情况是视图模型中有很多布尔值:ShowAShowBShowC 等,其中 A、B、C 是动态显示的。这很快就变得难以支持,尤其是对于所有交叉通知(更改一个属性需要为许多其他属性发出通知)。我正在考虑通过将“显示”逻辑移动到视图中来降低复杂性,然后视图模型不需要关心可见性,例如设置HasStatistic 就是它的全部工作。我试图提前考虑一下,您的解决方案几乎是完美的,也许它缺乏一点抛光才能变得可重用。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-08-24
      • 2020-08-30
      • 1970-01-01
      • 2012-08-09
      • 1970-01-01
      • 2013-04-02
      相关资源
      最近更新 更多