C#+ WPF:我应该怎么做适当的检查/卫士/访问控制值时施放?怎么做、卫士、访问控制、适当

2023-09-06 22:50:31 作者:繁华锦绣人不知

这大概是pretty的基础,但我只是捡了C#经过多年与其他语言的工作,我也很遗憾习惯于松散,动态类型。现在,在构建一个WPF的形​​式有很多的复选框,我发现我的code做这样确定复选框被选中是否实际上是相当复杂的简单的事情,因为需要检查空值和铸造的结果。我结束了辅助功能,如:

 私人布尔器isChecked(CheckBox控件){
        返回控制= NULL和放大器;!&安培; control.IsChecked = NULL和放大器;!&安培; control.IsChecked.HasValue&功放;&安培; (布尔)control.IsChecked;
    }
 

所以,在我的逻辑code,我可以做

 如果(器isChecked(opticsCheckBox))
    {
        //无论我需要做的,如果opticsCheckBox检查
    }
 
DevExpressv15.1 WPF控件升级 一

这是做事情在C#中(与WPF)的正常方式,还是我失去了一些东西简单吗?基本上,我发现条件语句的嵌套层所有的时间来检查空每个对象是坏code(和事实,我会忘记检查)的警告标志。不知道我应该做的事,但。

我应该使用的try ... catch无处不在,即使控制不是present或者检查是不是一个真正的异常情况?这似乎对我来说,这将最终被同样混乱。

另外一个例子来阐明: 当我想写这样的:

  maxWeight =(INT)maxWeightComboBox.SelectedItem;
 

我发现自己不是写:

 如果(maxWeightComboBox = NULL和放大器;!&安培;!maxWeightComboBox.SelectedItem = NULL)
    {
        ComboBoxItem项目=(ComboBoxItem)maxWeightComboBox.SelectedItem;
        maxWeight = Int32.Parse(item.Content.ToString());
    }
 

解决方案

WPF提供了这样的功能,如对财产的变化,依赖化子性质的,和有约束力的通知。 所以在WPF中的好的做法是使用直接访问控制presentationModel - 视图模式或MVC模式来代替。

您presentation模型(或位指示)必须处理所有的业务逻辑,并认为只是反映了模型的实际状态。

在你的情况的模型是这样的:

 公共类SampleModel:ObservableObject
{
    私人布尔? _isFirstChecked;
    公共BOOL? IsFirstChecked
    {
        得到
        {
            返回this._isFirstChecked;
        }
        组
        {
            如果(this._isFirstChecked!=值)
            {
                this._isFirstChecked =价值;
                this.OnPropertyChanged(IsFirstChecked);
            }
        }
    }

    私人诠释_maxWeight;
    公众诠释MaxWeight
    {
        得到
        {
            返回this._maxWeight;
        }
        组
        {
            如果(this._maxWeight!=值)
            {
                this._maxWeight =价值;
                this.OnPropertyChanged(MaxWeight);
            }
        }
    }

    公开的IEnumerable< INT> ComboBoxItems
    {
        得到
        {
            收益率回归123;
            收益率回归567;
            收益率回归999;
            收益率回归567;
            收益回报1999;
            收益回报5767;
            收益回报9990;
        }
    }
}
 

由于我们不得不通知视图属性更改事件,我们添加可观察类,它实现这样的逻辑:

 公共类ObservableObject:INotifyPropertyChanged的
{
    公共事件PropertyChangedEventHandler的PropertyChanged;
    保护无效OnPropertyChanged(字符串propertyName的)
    {
        VAR safePropertyChanged = this.PropertyChanged;
        如果(safePropertyChanged!= NULL)
        {
            safePropertyChanged(这一点,新PropertyChangedEventArgs(propertyName的));
        }
    }
}
 

所以,现在我们有申报的必要的属性presentation模式,让我们在视图中看到:

 <窗​​口x:类=Test.MainWindow
    的xmlns =htt​​p://schemas.microsoft.com/winfx/2006/xaml/$p$psentation
    的xmlns:X =htt​​p://schemas.microsoft.com/winfx/2006/xaml
    的xmlns:自=CLR的命名空间:测试
    标题=主窗口
    高度=350宽度=525>
< Window.Resources>
    <自:NullableBoolToStringConvreter X:关键=nullableBoolToStringConverter/>
< /Window.Resources>
<电网>
    < StackPanel的>
        < StackPanel的方向=横向>
            <标签VerticalAlignment =中心> IsFirstChecked:LT; /标签>
            <复选框VerticalAlignment =中心
                      =器isChecked{绑定路径= IsFirstChecked}/>
        < / StackPanel的>

        < StackPanel的方向=横向>
            <标签VerticalAlignment =中心>最大重量:LT; /标签>
            <组合框的ItemsSource ={绑定路径= ComboBoxItems}
                      VerticalAlignment =中心
                      的SelectedValue ={绑定路径= MaxWeight}>
            < /组合框>
        < / StackPanel的>

        <文本框文本={绑定路径= MaxWeight}/>
        <文本框文本={绑定路径= IsFirstChecked,转换器= {的StaticResource nullableBoolToStringConverter}}/>

        <按钮单击=Button_ClickCONTENT =重置组合框999和框为空/>
    < / StackPanel的>
< /网格>
 

另外,我们有落后修改此XAML code:

 公共部分类主窗口:窗口
{
    公共主窗口()
    {
        的InitializeComponent();

        VAR模型=新的SampleModel();
        model.MaxWeight = 5767;

        this.Model =模型;
    }

    公众的SampleModel型号
    {
        得到
        {
            返程(SampleModel中)this.DataContext;
        }
        组
        {
            this.DataContext =价值;
        }
    }

    私人无效Button_Click(对象发件人,RoutedEventArgs E)
    {
        this.Model.MaxWeight = 999;
        this.Model.IsFirstChecked = NULL;
    }
}
 

正如你可以看到,我们创建的SampleModel例如在主窗口的构造,设置其属性,并设置模型实例作为视图的DataContext。

DataContext的变更后,WPF内部mechanizm启动绑定过程。例如,对于ComboBox控件它提取模型属性ComboBoxItems并创建项目容器。然后提取属性的MaxValue并将其绑定到的SelectedValue,即组合框的选择将指向'5767'的价值。

在笔画演示的目的,我把两个文本框,其中显示MaxWeight和IsFirstChecked属性​​的实际值。默认绑定实现显示了空值空字符串,所以我们要添加适当的转换器:

 公共类NullableBoolToStringConvreter:的IValueConverter
{
    私人静态只读字符串_NullString =空;

    公共对象转换(对象的值,类型TARGETTYPE,对象参数,System.Globalization.CultureInfo文化)
    {
        返回值== NULL? NullableBoolToStringConvreter._NullString:value.ToString();
    }

    公共对象ConvertBack(对象的值,类型TARGETTYPE,对象参数,System.Globalization.CultureInfo文化)
    {
        抛出新的NotImplementedException();
    }
}
 

您可以测试应用程序,并确保用户界面的变化自动控制状态反映在模型中。另外,点击按钮将模特属性重置为定义的值和用户界面立刻反应就可以了。

因此​​,与WPF你​​不需要访问控制。 XAML和的InitializeComponent()向你保证,所有的控件创建。

至于检查:

  control.IsChecked.HasValue和放大器;&安培; (布尔)control.IsChecked
 

正如提到的,你可以使用EX pression

  model.IsFirstChecked?假
 

或扩展方法:

 公共静态类BooleanNullableExtensions
{
    公共静态布尔IsTrue运算(这可空<布尔>值)
    {
        返回value.HasValue和放大器;&安培; value.Value;
    }
}
 

This is probably pretty basic, but I'm just picking up C# after many years working with other languages, and I've unfortunately grown used to loose and dynamic typing. Now, in building a WPF form with a lot of checkboxes, I notice that my code to do simple things like determine whether a checkbox is checked is actually quite complicated due to the need to check for nulls and cast the results. I end up with helper functions like:

    private bool isChecked(CheckBox control) {
        return control != null && control.IsChecked != null && control.IsChecked.HasValue && (bool)control.IsChecked;
    }

so that in my logic code, I can just do

    if (isChecked(opticsCheckBox))
    {
        // whatever I need to do if opticsCheckBox is checked
    }

Is this the normal way of doing things in C# (with WPF), or am I missing something simple? Basically I'm finding the nested layers of conditionals to check every object for null all the time to be a warning sign of bad code (and the fact that I could forget a check). Not sure what I should be doing though.

Should I be using try ... catch everywhere even though the control not being present or checked isn't really an exceptional condition? That seems to me like it would end up being just as cluttered.

Another example to clarify: When I would like to write something like:

    maxWeight = (int)maxWeightComboBox.SelectedItem;

I find myself instead writing:

    if (maxWeightComboBox != null && maxWeightComboBox.SelectedItem != null)
    {
        ComboBoxItem item = (ComboBoxItem)maxWeightComboBox.SelectedItem;
        maxWeight = Int32.Parse(item.Content.ToString());
    }

解决方案

WPF provides such features as notifications on property changes, dependency propeties, and binding. So the good practice in WPF is to use PresentationModel-View pattern or MVC pattern instead of direct access to controls.

Your presentation model (or contoller) have to handle all business logic, and view just reflect actual state of model.

In your case model looks like:

public class SampleModel : ObservableObject
{
    private bool? _isFirstChecked;
    public bool? IsFirstChecked
    {
        get
        {
            return this._isFirstChecked;
        }
        set
        {
            if (this._isFirstChecked != value)
            {
                this._isFirstChecked = value;
                this.OnPropertyChanged("IsFirstChecked");
            }
        }
    }

    private int _maxWeight;
    public int MaxWeight
    {
        get 
        {
            return this._maxWeight;
        }
        set 
        {
            if (this._maxWeight != value)
            {
                this._maxWeight = value;
                this.OnPropertyChanged("MaxWeight");
            }
        }
    }

    public IEnumerable<int> ComboBoxItems
    {
        get
        {
            yield return 123;
            yield return 567;
            yield return 999;
            yield return 567;
            yield return 1999;
            yield return 5767;
            yield return 9990;
        }
    }
}

As we have to notify view with property changed event, we add Observable class, which implement this logic:

public class ObservableObject : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string propertyName)
    {
        var safePropertyChanged = this.PropertyChanged;
        if (safePropertyChanged != null)
        {
            safePropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }           
    }
}

So, now we have presentation model with declaration of necessary properties, let's see at view:

<Window x:Class="Test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:self ="clr-namespace:Test"
    Title="MainWindow" 
    Height="350" Width="525">
<Window.Resources>
    <self:NullableBoolToStringConvreter x:Key="nullableBoolToStringConverter" />
</Window.Resources>
<Grid>
    <StackPanel>
        <StackPanel Orientation="Horizontal">
            <Label VerticalAlignment="Center">IsFirstChecked:</Label>
            <CheckBox VerticalAlignment="Center"
                      IsChecked="{Binding Path=IsFirstChecked}" />
        </StackPanel>

        <StackPanel Orientation="Horizontal">
            <Label VerticalAlignment="Center">Max Weight:</Label>
            <ComboBox ItemsSource="{Binding Path=ComboBoxItems}" 
                      VerticalAlignment="Center"
                      SelectedValue="{Binding Path=MaxWeight}">
            </ComboBox>
        </StackPanel>

        <TextBox Text="{Binding Path=MaxWeight}" />
        <TextBox Text="{Binding Path=IsFirstChecked, Converter={StaticResource nullableBoolToStringConverter}}"/>

        <Button Click="Button_Click" Content="Reset combo box to 999 and checkbox to null"/>
    </StackPanel>
</Grid>

Also we have to modify this xaml code behind:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        var model = new SampleModel();
        model.MaxWeight = 5767;

        this.Model = model;
    }

    public SampleModel Model
    {
        get
        {
            return (SampleModel)this.DataContext;   
        }
        set 
        {
            this.DataContext = value;
        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        this.Model.MaxWeight = 999;
        this.Model.IsFirstChecked = null;
    }
}

As you can see we create SampleModel instance at MainWindow constructor, set up its properties and set model instance as DataContext of view.

After DataContext changed, WPF internal mechanizm starts binding process. For example, for combobox control it extracts model property ComboBoxItems and creates item containers. Then extracts property MaxValue and bind it to SelectedValue, i.e. combobox selection will point at value '5767'.

In demostration purposes I placed two text boxes, which display actual value of "MaxWeight" and "IsFirstChecked" properties. Default binding implementation shows empty strings on null values, so we have to add appropriate converter:

public class NullableBoolToStringConvreter : IValueConverter
{
    private static readonly string _NullString = "Null";

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value == null ? NullableBoolToStringConvreter._NullString : value.ToString();
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

You can test application and ensures that changes of UI controls state automatically reflects in model. On the other hand, clicking on the button will reset model properties to defined values and UI immediatly reacts on it.

So with WPF you don't need access to controls. XAML and InitializeComponent() guarantee you that all controls are created.

As for checking:

control.IsChecked.HasValue && (bool)control.IsChecked

as was mentioned you can use expression

model.IsFirstChecked ?? false

or extension method:

public static class BooleanNullableExtensions 
{
    public static bool IsTrue(this Nullable<bool> value)
    {
        return value.HasValue && value.Value;
    }
}