6

我正在为以下问题寻找一个优雅的解决方案。

假设我们有一个 (View)Model 具有以下布尔属性:

  • Α
  • 贝塔
  • 伽玛
  • 三角洲

接下来,我在表面上有 5 个控件,这些控件仅在满足基于这些属性的条件时才可见。当然,一旦这些属性之一更新,更改应该立即传播:

  • ControlA -> Alpha && ( Beta || Gamma )
  • ControlB -> 三角洲
  • ControlC -> 三角洲 || 贝塔
  • ControlD -> Gamma && Alpha && Delta
  • ControlE -> 阿尔法 || 伽玛

到目前为止,我想出的唯一解决方案是使用 MultiValueConverters。

ControlA 的示例:

<ControlA>
   <ControlA.Visibility>
      <MultiBinding Converter={StaticResource ControlAVisibilityConverter}>
          <Binding Path="Alpha"/>
          <Binding Path="Beta"/>
          <Binding Path="Gamma"/>
      </MultiBinding>
   </ControlA.Visibility>
</ControlA>

此 ControlAVIsibilityConverter 检查条件“Alpha && (Beta || Gamma)”并返回适当的值。

它确实有效......好吧......但也许你可以想出一个更优雅的解决方案?

谢谢你,双习惯

4

3 回答 3

5

在这种情况下,为每个规则编写一个转换器会将您的业务逻辑放在两个位置(在转换器和视图模型中)。我建议使用 INotifyPropertyChanged 事件为 ViewModel 中的每个控件创建一个属性/标志,以决定该控件是否可见(或其他行为)。

请注意,当您查看我的视图模型(如下)时,您会看到我公开了 bool 和 Visibilty 类型的属性。

如果您需要将该属性用作一般规则,请使用 bool 和 DataTrigger。

public bool ControlD

如果您只需要控制可见性,您可以直接绑定到可见性:

public Visibility ControlA

更新:由于@Wallstreet Programmer 的评论,我添加了另一个选项来使用 BooleanVisibilityConverter。我更新了下面的第五个控件以反映如何使用转换器。我在底部添加了转换器的代码。

这是 XAML 中的测试窗口:

<Window x:Class="ControlVisibleTrigger.Views.MainView"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Main Window" Height="400" Width="800">
  <Window.Resources>
    <Style x:Key="DropDownStyle" TargetType="TextBox">
        <Setter Property="Visibility" Value="Hidden"/>
        <Style.Triggers>
            <DataTrigger Binding="{Binding ControlC}" Value="True">
                <Setter Property="Visibility" Value="Visible"/>
            </DataTrigger>
        </Style.Triggers>
    </Style>
  </Window.Resources>
  <DockPanel>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <StackPanel Grid.Row="0">
            <CheckBox IsChecked="{Binding Path=Alpha,Mode=TwoWay}" Content="Alpha"/>
            <CheckBox IsChecked="{Binding Path=Beta,Mode=TwoWay}" Content="Beta"/>
            <CheckBox IsChecked="{Binding Path=Gamma,Mode=TwoWay}" Content="Gamma"/>
            <CheckBox IsChecked="{Binding Path=Delta,Mode=TwoWay}" Content="Delta"/>
        </StackPanel>
        <TextBox Grid.Row="1" Visibility="{Binding Path=ControlA}" Text="Binding to visibility"/>
        <Button Grid.Row="2" Visibility="{Binding Path=ControlB}" Content="Binding to visibility"/>
        <TextBox Grid.Row="3" Style="{StaticResource DropDownStyle}" Text="Using WindowResource DataTrigger"/>
        <TextBox Grid.Row="4" Text="Using Local DataTrigger">
            <TextBox.Style>
              <Style TargetType="TextBox">
                <Setter Property="Visibility" Value="Hidden"/>
                <Style.Triggers>
                    <DataTrigger Binding="{Binding ControlD}" Value="True">
                        <Setter Property="Visibility" Value="Visible"/>
                    </DataTrigger>
                </Style.Triggers>
              </Style>
            </TextBox.Style>
        </TextBox>
        <Button Grid.Row="5" 
                Content="Press me" 
                Visibility="{Binding Path=ControlE, Converter={StaticResource booleanVisibilityConverter}, ConverterParameter=True, Mode=OneWay}">
    </Grid>
  </DockPanel>
</Window>

这是视图模型:

public class MainViewModel : ViewModelBase
{
  public MainViewModel()
  {
  }

  private bool _alpha = true;
  public bool Alpha
  {
     get
     {
        return _alpha;
     }
     set
     {
        _alpha = value;
        OnPropertyChanged("ControlA");
        OnPropertyChanged("ControlB");
        OnPropertyChanged("ControlC");
        OnPropertyChanged("ControlD");
        OnPropertyChanged("ControlE");
     }
  }

  private bool _beta = true;
  public bool Beta
  {
     get
     {
        return _beta;
     }
     set
     {
        _beta = value;
        OnPropertyChanged("ControlA");
        OnPropertyChanged("ControlB");
        OnPropertyChanged("ControlC");
        OnPropertyChanged("ControlD");
        OnPropertyChanged("ControlE");
     }
  }

  private bool _gamma = true;
  public bool Gamma
  {
     get
     {
        return _gamma;
     }
     set
     {
        _gamma = value;
        OnPropertyChanged("ControlA");
        OnPropertyChanged("ControlB");
        OnPropertyChanged("ControlC");
        OnPropertyChanged("ControlD");
        OnPropertyChanged("ControlE");
     }
  }

  private bool _delta = true;
  public bool Delta
  {
     get
     {
        return _delta;
     }
     set
     {
        _delta = value;
        OnPropertyChanged("ControlA");
        OnPropertyChanged("ControlB");
        OnPropertyChanged("ControlC");
        OnPropertyChanged("ControlD");
        OnPropertyChanged("ControlE");
     }
  }

  public Visibility ControlA
  {
     get
     {
        Visibility result = Visibility.Hidden;
        if ( Alpha && (Beta || Gamma))
        {
           result = Visibility.Visible;
        }
        return result;
     }
  }

  public Visibility ControlB
  {
     get
     {
        Visibility result = Visibility.Hidden;
        if ( Delta )
        {
           result = Visibility.Visible;
        }
        return result;
     }
  }

  private bool _controlC = false;
  public bool ControlC
  {
     get
     {
        return Delta || Beta;
     }
  }

  private bool _controlD = false;
  public bool ControlD
  {
     get
     {
        return Gamma && Alpha && Delta;
     }
  }

  private bool _controlE = false;
  public bool ControlE
  {
     get
     {
        return Alpha || Gamma;
     }
  }
}

这是转换器:

public class BooleanVisibilityConverter : IValueConverter
{
  public object Convert( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
  {
    if( ( value == null ) || ( !( value is bool ) ) )
      return Binding.DoNothing;

    Visibility elementVisibility;
    bool shouldCollapse = ( ( bool )value );

    if( parameter != null )
    {
      try
      {
        bool inverse = System.Convert.ToBoolean( parameter );

        if( inverse )
          shouldCollapse = !shouldCollapse;
      }
      catch
      {
      }
    }

    elementVisibility = shouldCollapse ? Visibility.Collapsed : Visibility.Visible;
    return elementVisibility;
  }

  public object ConvertBack( object value, Type targetType, object parameter, System.Globalization.CultureInfo culture )
  {
    throw new NotImplementedException();
  }
}
于 2010-09-22T14:38:47.187 回答
3

假设是否应显示控件有业务逻辑原因,我肯定会将逻辑存储为 ViewModel 中的布尔值(尽管我会根据业务逻辑对其进行命名,例如:CriteriaA not ControlAVIsible)。这允许进行简单的单元测试,以确保将 Criteria 正确设置为 alpha、beta、gamma 和 delta 更改。除此之外,我同意华尔街程序员的回答(尽管我没有代表评论或投票他的回答)。

于 2010-09-24T14:04:20.110 回答
0

如果控件支持命令(例如,如果它们是按钮),请使用命令模式。使用RelayCommand(查找),您可以使用 lambda 表达式指定启用控件的条件(这正是您所需要的)。不过,它需要一些代码隐藏。

于 2010-09-21T21:53:02.413 回答