WPF - طريقة أنيقة لتعطيل وتمكين عناصر تحكم مختلفة بناءً على حالات مختلفة من النموذج باستخدام MVVM
-
04-10-2019 - |
سؤال
أنا أبحث عن حل أنيق للمشكلة التالية.
لنفترض أن لدينا نموذج (عرض) مع خصائص منطقية التالية:
- ألفا
- بيتا
- جاما
- دلتا
بعد ذلك ، لدي 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 (وهو بالضبط ما تحتاجه). إنه يحتاج إلى بعض الكود-رغم ذلك.