WPF - طريقة أنيقة لتعطيل وتمكين عناصر تحكم مختلفة بناءً على حالات مختلفة من النموذج باستخدام MVVM

StackOverflow https://stackoverflow.com/questions/3764484

سؤال

أنا أبحث عن حل أنيق للمشكلة التالية.

لنفترض أن لدينا نموذج (عرض) مع خصائص منطقية التالية:

  • ألفا
  • بيتا
  • جاما
  • دلتا

بعد ذلك ، لدي 5 عناصر تحكم على السطح والتي يجب أن تكون مرئية فقط عند استيفاء حالة تعتمد على تلك الخصائص. بالطبع ، بمجرد تحديث أحد هذه الخصائص ، يجب نشر التغيير فوريًا:

  • Controla -> alpha && (beta || gamma)
  • ControlB -> دلتا
  • Controlc -> Delta || بيتا
  • Controld -> Gamma && alpha && Delta
  • Controle -> alpha || جاما

الحل الوحيد الذي توصلت إليه حتى الآن هو استخدام multivalueConverters.

مثال على Controla:

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

يتحقق ControlVisibilityConverter من حالة "Alpha && (Beta || gamma)" ويعيد القيمة المناسبة.

إنه يعمل .. حسنًا .. ولكن ربما يمكنك التوصل إلى حل أكثر أناقة؟

شكرا لك ، twinhabit

هل كانت مفيدة؟

المحلول

تضع كتابة محول لكل قاعدة منطق عملك في مكانين في هذه الحالة (في المحول ونموذج العرض). أقترح إنشاء خاصية/علامة لكل عنصر تحكم في ViewModel الخاص بك مع أحداث InotifyPropertyChanged لتحديد ما إذا كان التحكم مرئيًا (أو سلوك آخر).

لاحظ أنه عندما تنظر إلى ViewModel (أدناه) ، سترى أنني أعرض خصائص من النوع Bool و Visibilty.

إذا كنت بحاجة إلى استخدام الخاصية كقاعدة عامة ، فاستخدم Bool و Datatrigger.

public bool ControlD

إذا كنت بحاجة فقط إلى التحكم في الرؤية ، يمكنك ربط الرؤية مباشرة:

public Visibility ControlA

تحديث: بسبب التعليق من قبل WallStreet Programmer ، أضفت خيارًا آخر لاستخدام BooleAnvibilityConverter. لقد قمت بتحديث عنصر التحكم الخامس أدناه لتعكس كيفية استخدام المحول. أضفت رمز المحول في الأسفل.

فيما يلي نافذة اختبار في 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>

هنا هو ViewModel:

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

نصائح أخرى

على افتراض أن هناك سببًا منطقيًا تجاريًا حول ما إذا كان ينبغي عرض عناصر التحكم أم لا ، فمن المؤكد أن يكون المنطق المخزّن كطرف في ViewModel (على الرغم من أنني سأسميه وفقًا لمنطق العمل على سبيل المثال: معايير غير مستحيلة). يتيح هذا اختبار الوحدة السهل لضمان ضبط المعايير بشكل صحيح مع تغيير Alpha و Beta و Gamma و Delta. بخلاف ذلك ، أوافق على إجابة مبرمجي WallStreet (على الرغم من أنه ليس لدي مندوب للتعليق أو التصويت على رده).

إذا كانت أوامر دعم عناصر التحكم (على سبيل المثال إذا كانت أزرار) ، فاستخدم نمط الأوامر. مع RelayCommand (ابحث عن ذلك) ، يمكنك تحديد الحالة التي يتم بموجبها تمكين التحكم بتعبير Lambda (وهو بالضبط ما تحتاجه). إنه يحتاج إلى بعض الكود-رغم ذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top