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