多个绑定以显示动态内容

时间:2018-01-30 13:35:48

标签: c# wpf layout mvvm binding

使用动态内容创建布局时,我经常会这样做:

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

但它并不总是可行/容易且有缺点。我必须监视所有条件属性的更改并提高结果属性的通知。如果其中一个条件属性是静态的或特定于视图的,则添加事件处理程序,订阅/取消订阅等不可避免的麻烦,以使其在viewmodel和/或上升通知中可用。

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

问题:管理多重绑定用于创建动态内容布局的最佳(可重用,易用,简短,易于理解)的方法是什么?我可能缺乏找到类似问题的正确词语。

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

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

也许我错过了一些明显而简单的东西?

2 个答案:

答案 0 :(得分:1)

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

如下所示:

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>

在您有多个标志以确定可见性的任何区域都可以高度重复使用,而且它也可以进行单元测试。

答案 1 :(得分:0)

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

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

这个想法是使用几个附加属性进行绑定,并将所需属性绑定到&#34;结果&#34;,每次更改某些内容时都会重新计算(类似于多重绑定)。

将公式指定为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=...}" />

注意:

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