Page 30 - MSDN Magazine, May 2019
P. 30
Business rules, by the way, are different from data validation. Responding to data entry and checking for text length and numeric ranges is simply validation. Rules govern types of user behavior.
For example, a bank has a business rule not to give a loan to customers with a credit score below a particular value. A plumber has a rule not to travel to a customer outside a certain ZIP code. Rules are about behavior. In some cases, rules change every sin- gle day, like what credit scores influence new loans. In other cases, rules never change, such as how a mechanic won’t ever work on a Subaru older than 2014.
Now, consider this acceptance criteria: A user can’t click the Button until the CheckBox is checked. This is a rule and it’s as close to immutable as you can get. I’m going to implement it close to my controls:
<StackPanel Padding="20">
<TextBlock>Lorem ipsum.</TextBlock>
<CheckBox x:Name="AgreeCheckBox">I agree!</CheckBox> <Button IsEnabled="{Binding Path=IsChecked,
ElementName=AgreeCheckBox}">Submit1</Button>
<Button IsEnabled="{x:Bind Path=AgreeCheckBox.IsChecked.Value,
Mode=OneWay}">Submit2</Button> </StackPanel>
In this code, data binding perfectly satisfies my requirement. The Submit1 Button uses classic WPF (and UWP) data binding. The Submit2 Button uses modern UWP data binding.
Notice in Figure 3 that Submit2 is enabled. Is this right? Well, in the Visual Studio Designer, classic data binding has the advan- tage of rendering at design time. For now, compiled data binding (x:Bind) only occurs at run time. Choosing between classic and compiled data binding is the most difficult easy decision you’re going to make. On the one hand, compiled binding is fast. But, on the other, classic binding is simple. Compiled binding exists to address XAML’s difficult performance problem: data binding. Because classic binding requires runtime reflection, it’s inherently slower, struggling to scale.
Many new features have been added to classic binding, such as asynchronous binding; and several patterns have emerged to help developers. Yet, as UWP postured to succeed WPF, it suffered from the same dragging issue. Here’s something to think about: The ability to use classic binding in an asynchronous mode was not ported to UWP from WPF. Read into that what you want, but it does encourage enterprise developers to invest in compiled binding. Compiled binding leverages the XAML code generator, creating the codebehind automatically and coupling the binding statements with real properties and datatypes expected at run time.
Because of this coupling, mismatched types can create errors, as can attempting to bind to anonymous objects or dynamic JSON
Figure 3 Implementing a Business Rule with Data Binding 24 msdn magazine
objects. These edge cases aren’t missed by many developers, but they’re gone:
• Compiled binding resolves the performance issues of data binding while introducing certain constraints.
• Backward compatibility maintains classic binding support while giving UWP developers a better option.
• Innovation and improvements to data binding are invested into compiled binding, not classic binding.
• Features like function binding are available only with compiled binding where Microsoft’s binding strategy is clearly focused. Yetthesimplicityandthedesign-timesupportofclassicbinding keeps the argument alive, pressing the Microsoft developer tooling team to continue to improve compiled binding and its developer experience. Note that in this article, choosing one or the other will have a near-immeasurable impact. Some of the samples will demonstrate classic binding while others show compiled binding. It’s up to you to decide. The decision, of course, is most meaningful
in large apps.
Custom Events Custom events can’t be declared in XAML, so
you handle them in your codebehind. For example, I can forward the click event of the submit button to a custom click event on my user control:
public event RoutedEventHandler Click; public MyUserControl1()
{
InitializeComponent(); SubmitButton.Click += (s, e) => Click?.Invoke(this, e);
}
Here, the code raises the custom events, forwarding the Routed- EventArgs from the button. Consuming developers can handle these events declaratively, like every other event in XAML:
<controls:MyUserControl1 Click="MyUserControl1_Click" />
The value of this is that consuming developers don’t need to learn a new paradigm; custom controls and out-of-the box first-party controls behave functionally the same.
Custom Properties To let consuming developers supply their own EULAs, I can set the x:FieldModifier attribute on the Text- Block. This modifies XAML compilation behavior from the default private value:
<TextBlock x:Name="EulaTextBlock" x:FieldModifier="public" />
But easy doesn’t mean good. This method offers little abstrac- tion and requires developers to understand the internal structure. It also requires codebehind. So, I’ll avoid using the attribute approach in this case:
public string Text {
get => (string)GetValue(TextProperty);
set => SetValue(TextProperty, value); }
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(nameof(Text), typeof(string),
typeof(MyUserControl1), new PropertyMetadata(string.Empty));
Equally easy—and without the caveats—is a dependency property data-bound to the TextBlock’s Text property. This allows the con- sumingdevelopertoread,writeorbindtothecustomTextproperty:
<StackPanel Padding="20">
<TextBlock Text="{x:Bind Text, Mode=OneWay}" /> <CheckBox>I agree!</CheckBox> <Button>Submit</Button>
</StackPanel>
XAML