UWP: compile-Type Binding x: Bind, uwpbind
In UWP development, in addition to the traditional Binding, we can also use the new x: Bind, since the latter performs initialization during program compilation (unlike Binding, which is created and initialized at runtime), we can call it x: Bind as a compilation Binding, just like the title of this article.
X: Bind is introduced because it has many advantages over traditional Binding, such:
- Better performance;
- Compilation error;
- Easy debugging:
- Easy to use (bound to functions, events, etc)
In view of the above advantages of x: Bind, we recommend that you use it in your project.Use it whenever possibleOf course, compared with Binding, it also lacks some features, so you still need to use traditional Binding when necessary. In other words, you can use the two binding methods in a project. Statement again: we recommend that you use x: Bind whenever possible, unless x: Bind cannot complete the operation you want, you should consider using Binding.
Here I will explain in detail the usage of x: Bind and the advantages mentioned above. This article assumes that you have mastered (or at least understood) the basic knowledge of data binding in WPF/UWP. If you do not know data binding before continuing to learn the following, it is recommended that you understand the relevant knowledge (I believe most XAML developers are fine ).
X: Bind Data Source
The difference from traditional binding is that the data source of x: Bind is the current View (that is, the Page or user control UserControl), that is, it uses the Page or User Control instance as the data source. Therefore, if you set the Path attribute, x: bind will go to the current Code-Bebind class to find the corresponding name Member (attribute, field, method ). In the following example, x: Bind finds and binds its InfoA attribute in the current user control instance.
<UserControl x:Class="xBindTest.Controls.BindingModeControl"... <TextBlock Text="{x:Bind InfoA}" /> ...</UserControl>
public sealed partial class BindingModeControl : UserControl, INotifyPropertyChanged { public string InfoA { get; set; } }
By the way, if the InfoA attribute cannot be found, compilation will fail. This is one of the advantages of x: Bind. It provides compilation errors, unlike Binding, the error message is displayed only in the Output window of.
In traditional Binding, Binding data sources can be specified in four forms: DataContext (default), RelativeSource, Source, and ElementName. However, since the x: Bind uses the current View instance as the only data source, we do not need to set DataContext like the traditional Binding. For the following three ways to set the data source, x: bind only supports the following two cases:
- ElementName
X: Bind->{X: Bind slider1.Value}
Binding->{Binding Value, ElementName = slider1}
- RelativeSource: Self
X: Bind-><Rectangle x: Name = "rect1" Width = "200" Height = "{x: Bind rect1.Width}".../>
Binding-><Rectangle Width = "200" Height = "{Binding Width, RelativeSource = {RelativeSource Self}".../>
Note: In the preceding example, both slider1 and rect1 are controls in the current View. They are essentially the fields of the current View, so they can be used directly in x: Bind. In addition to the preceding two cases, x: Bind is not supported for Source and other forms of RelativeSource.
Binding Mode)
Next, let's look at the binding mode of x: Bind.
X: Bind Binding Mode values include OneWay, OneTime, and TwoWay;The default value is OneTime.It is very important to remember this, because in the development process, binding is not as normal as we think, because the Mode is not set as a proper value. OneTime means to initialize binding on the interface only during interface initialization. This is also the reason why x: Bind is more optimized. By the way, the Default value of the Mode attribute of the traditional Binding is Default (this value indicates that the read-only control is OneWay and the Editable control is TwoWay ).
Specifically, the binding of x: Bind is initiated in the Loading event of Page or User Control. That is, when Mode = OneTime (default, only when a property value is set in the View Constructor (before the Loading event) will it be updated to the UI in the x: Bind initialization; modifying the value of this attribute in other locations (such as Loaded events or response events of an operation) will not be updated (even if the PropertyChanged event in INotifyPropertyChanged is called ). Refer to the following code:
<TextBlock Margin="{StaticResource ContentMargin}" Text="{x:Bind InfoA}" />
public BindingModeControl(){ InfoA = "InfoA: Value for x:Bind (Mode=One Time)";}
If Mode = OneWay is set, an association is created when the binding is initiated. When the value of the binding source is changed, the binding target (UI) is updated in a timely manner. Refer to the following code:
<TextBlock VerticalAlignment="Center" Text="{x:Bind InfoB, Mode=OneWay}" />
private void btnUpdateValueForOneWay_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e){ InfoB = "InfoB: Value Updated";}
Now let's reconsider the first case. If we haven't set the Mode for the binding, it uses the default value OneTime. In this case, what should we do if we really want to update the UI by updating the property value other than the constructor? Here we need to use the Bindings object of the current View.
If the current View uses x: Bind, it will have a field Bindings, which is generated in the obj folder.<Viewname>. g. csFile. There are three methods:
- Update () call this method to Update all x: Bind bound values in the current View
- When Initialize () calls this method, it will determine whether the binding is initialized; if not, it will directly call the Update method. If it has been initialized, nothing will be done;
- StopTracking () removes the Listeners created when the OneWay and TwoWay bindings are initialized, that is, the View does not listen for updates to the attribute values;
When a property value is modified, even if it is in the OneTime binding mode, the UI can be updated through the Bindings Update method. Refer to the following code:
<TextBlock VerticalAlignment="Center" Text="{x:Bind InfoC}" />
private void btnUpdateValueForOneTime_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e){ InfoC = "InfoC: Value updated by this.Bindings.Update() method"; this.Bindings.Update();}
Converting)
When the data source attribute type is inconsistent with the bound target attribute type, if we use the traditional Binding, we can set an object that implements IValueConverter to the Converter attribute of Binding to convert the value. In addition to this method, using x: Bind also makes it easier to Bind attributes to functions. That is to say, you can put a function in x: Bind. Of course, x: Bind still finds the specified function in the Code-Behind Code of the current View. Refer to the following code:
<Border x:Name="border" Background="{x:Bind GetBrush(IsPass), Mode=OneWay}"> <Image Margin="20" Source="{x:Bind GetImage(IsPass), Mode=OneWay}" /> </Border>
public Brush GetBrush(bool isPass) { return isPass ? new SolidColorBrush(Colors.LimeGreen) : new SolidColorBrush(Colors.Crimson); } // both public and private work well private ImageSource GetImage(bool isPass) { return isPass ? new BitmapImage(new Uri("ms-appx:///Assets/Happy.png")) : new BitmapImage(new Uri("ms-appx:///Assets/Sad.png")); }
In the preceding example, the bound functions both accept one parameter. In fact, multiple parameters are supported. Therefore, this is more convenient than IValueConverter. In addition, property binding to a function also supports bidirectional conversion similar to IValueConverter. In addition to conversion from the source type to the target type, it also supports conversion from the target type to the source type by specifying another method using the BindBack attribute.
Another convenient conversion is between Visibility and bool: the Visibility of the control can be directly bound to a Boolean attribute or field. When the Boolean value is true, the value of Visibility is Visible, and vice versa, it is Collapsed. Refer to the following code:
<Button Content="Logout" Visibility="{x:Bind IsLogin}" />
Finally, it should be noted that the above two conversion functions are only supported in the anniversary Update (14393/1607) and later versions.
Use in DataTemplate
DataTemplate is used to set the ItemTemplate attribute for list controls (such as ListView). If x: Bind is used in DataTemplate, what should I do?
First, specify x: DataType for DataTemplate and tell it the data Model class to be displayed. In general, this requires introducing the xmlns Command Space. Then, inside the DataTemplate, use x: bind is directly bound to the relevant attributes of the Model. Refer to the following code:
<UserControl ... xmlns:models="using:xBindTest.Models"> <UserControl.Resources> <DataTemplate x:Key="FriendItemTemplate" x:DataType="models:Friend"> <StackPanel Margin="0,4"> <TextBlock FontSize="20" FontWeight="SemiBold" Text="{x:Bind Name}" /> <TextBlock Margin="{StaticResource ContentMargin}" FontSize="14" Text="{x:Bind Email}" /> </StackPanel> </DataTemplate> </UserControl.Resources> <Grid> <ListView ItemTemplate="{StaticResource FriendItemTemplate}" ItemsSource="{x:Bind AllFriends}" /> </Grid></UserControl>
Compile to run normally. If you do not set x: ype for DataTemplate or bind a property that does not exist in the Model to the DataTemplate, compilation fails.
The above example references the ememplate resource in the current View. In actual project development, you may put resources in one or more ResourceDictionary files to facilitate resource organization. So how should the x: Bind DataTemplate be moved to the ResourceDictionary file?
First, the Code-Behind file must be available in the x: Bind XAML file. How can this problem be solved?
You can create a Page or UserControl, change its base class from Page or UserControl to ResourceDictionary, delete unnecessary and default added UI elements, and copy the DataTemplate resource items, you can. Refer to the following code:
<ResourceDictionary x:Class="xBindTest.Styles.DataTemplates" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:models="using:xBindTest.Models" mc:Ignorable="d"> <DataTemplate x:Key="AnotherFriendItemTemplate" x:DataType="models:Friend"> <StackPanel Margin="0,4"> <TextBlock FontSize="20" FontWeight="SemiBold" Text="{x:Bind Name}" /> <TextBlock Margin="{StaticResource ContentMargin}" FontSize="14" Text="{x:Bind Email}" /> </StackPanel> </DataTemplate></ResourceDictionary>
using Windows.UI.Xaml;namespace xBindTest.Styles{ public sealed partial class DataTemplates : ResourceDictionary { public DataTemplates() { this.InitializeComponent(); } }}
Bind to event
When using traditional binding, we usually use commands or actions to respond to control operations (when the control does not support Command or responds to a specific event of the control, such as the SelectionChanged event of the ListView control); however, x: Bind can easily perform the same operation, because it supports binding to events. Let's look at the Code:
<Button Click="{x:Bind ShowInfoTest1}" Content="Show Info" />
public void ShowInfoTest1(){ Info = "Update Info in method: ShowInfoTest1()";}
It is as simple as binding an attribute. The difference is that it is not a property but an event name, and Path is not a property name, but a method name. This operation is much simpler than ICommand or action.
Here, we need to add the signature of the method:
- The parameter can be blank, for example:
void ShowInfoTest1()
- It can also be consistent with the signature of the Bound event, for example:
void ShowInfoTest1(object sender, RoutedEventArgs e)
- It can also be the same as the number of event signatures. Each parameter type in the event signature can be converted to the parameter type defined in the method, for example:
void ShowInfoTest1(object sender, object e)
If they are inconsistent, the project will not pass during compilation.
In addition to the preceding flexible method signatures, x: Bind does not require the return values of methods. It can be void or any other return type; it also supports binding the async method.
MVVM
Basically, the main features of x: Bind are mentioned here. However, there is a problem: During UWP application development, we generally use the MVVM mode, while x: Bind uses the current View as the data source. How can we Bind it to a member of ViewModel? Simply add the ViewModel attribute to the Page or UserControl, whose type is the ViewModel of the corresponding View, and use multi-level Path in x: Bind. Refer to the following code:
public sealed partial class BindToEventControl : UserControl { public BindToEventControl() { this.InitializeComponent(); ViewModel = new BindToEventViewModel(); } public BindToEventViewModel ViewModel { get; set; } }
<Button Click="{x:Bind ViewModel.ShowInfoTest1}" /><TextBlock Text="{x:Bind ViewModel.Info, Mode=OneWay, TargetNullValue='(no value)'}" />
In addition, to decouple the View and ViewModel, you may use a class similar to ViewModelLocator to locate the ViewModel. In this case, how can we combine x: Bind?
First, you can retain the reference of the DataContext of the Page to Locator. You need to add a ViewModel to the Page as in the previous example. Refer to the following code:
<Page ... DataContext="{Binding HomeViewModel, Source={StaticResource Locator}}">
public sealed partial class HomePage : Page { public HomePage() { this.InitializeComponent(); } public HomeViewModel ViewModel => DataContext as HomeViewModel; }
Others
Note the following points when using x: Bind:
- X: Bind does not support UpdateSourceTrigger. Therefore, for TextBox, you can update the value of the binding source without losing the focus (by setting UpdateSourceTrigger = ProppertyChanged). In x: bind cannot be implemented; that is to say, you still need to use the traditional Binding;
- This article mentioned at the beginning that x: One of the advantages of Bind is ease of debugging. When you use x: Bind in the View, then in obj \ (x64/x86/ARM) \ <viewname>. g. the corresponding binding code is generated in the cs file. Here you can view the dynamically generated code and set breakpoints for debugging. I have not made any in-depth research on this, so I will not detail it here;
Summary
This article describes x: Bind in UWP, including its advantages and usage. It has advantages such as better performance, more convenient use, checking errors during compilation, and easier debugging. Therefore, we recommend that you use it in your project as much as possible. Of course, compared with the traditional Binding, it is also inferior, so you can still use the traditional Binding to complete the functions you want. Finally, the xBindTest and the source code are attached. It is a Demo I wrote for x: Bind. The Code referenced in this article can be found in almost all of this project.
References:
{X: Bind} markup extension
Source code download