- Mastering Windows Presentation Foundation
- Sheridan Yuen
- 1057字
- 2021-06-24 16:49:06
In UI controls
One another common way to include functionality in an application framework is to encapsulate it into custom controls. In doing so, we can expose the required functionality using Dependency Properties, while hiding the implementation details. This is also another great way to promote reusability and consistency throughout the application. Let's take a look at a simple example of a UserControl that wraps the functionality of the System.Windows.Forms.FolderBrowserDialog control:
<UserControl x:Class="CompanyName.ApplicationName.Views.Controls.FolderPathEditField" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:Controls="clr-namespace:CompanyName.ApplicationName.Views.Controls"> <TextBox Name="FolderPathTextBox" Text="{Binding FolderPath, RelativeSource={RelativeSource AncestorType={x:Type Controls:FolderPathEditField}}, FallbackValue='', UpdateSourceTrigger=PropertyChanged}" Cursor="Arrow" PreviewMouseLeftButtonUp="TextBox_PreviewMouseLeftButtonUp" /> </UserControl>
This simple UserControl just contains a textbox with its Text property data bound to the FolderPath Dependency Property that is declared in our control's code behind. Remember that it is perfectly acceptable to use the code behind of a UserControl for this purpose when using MVVM. Note that we have used a RelativeSource binding here because nothing has been set to this control's DataContext property. We'll find out much more about data binding in Chapter 4, Becoming Proficient with Data Binding, but for now, let's continue.
You may notice that we have attached a handler for the PreviewMouseLeftButtonUp event in the code behind and as no business-related code is being used there, this is also perfectly acceptable when using MVVM. The only other notable code here is that we set the Cursor property to show an arrow when users mouse over our control. Let's now take a look at the code behind of the UserControl and see how the functionality is encapsulated:
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using FolderBrowserDialog = System.Windows.Forms.FolderBrowserDialog; namespace CompanyName.ApplicationName.Views.Controls { public partial class FolderPathEditField : UserControl { public FolderPathEditField() { InitializeComponent(); } public static readonly DependencyProperty FolderPathProperty = DependencyProperty.Register(nameof(FolderPath), typeof(string), typeof(FolderPathEditField), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public string FolderPath { get { return (string)GetValue(FolderPathProperty); } set { SetValue(FolderPathProperty, value); } } public static readonly DependencyProperty OpenFolderTitleProperty = DependencyProperty.Register(nameof(OpenFolderTitle), typeof(string), typeof(FolderPathEditField), new FrameworkPropertyMetadata(string.Empty, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); public string OpenFolderTitle { get { return (string)GetValue(OpenFolderTitleProperty); } set { SetValue(OpenFolderTitleProperty, value); } } private void TextBox_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (((TextBox)sender).SelectedText.Length == 0 && e.GetPosition(this).X <= ((TextBox)sender).ActualWidth - SystemParameters.VerticalScrollBarWidth) ShowFolderPathEditWindow(); } private void ShowFolderPathEditWindow() { string defaultFolderPath = string.IsNullOrEmpty(FolderPath) ? Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments) : FolderPath; string folderPath = ShowFolderBrowserDialog(defaultFolderPath); if (string.IsNullOrEmpty(folderPath)) return; FolderPath = folderPath; } private string ShowFolderBrowserDialog(string defaultFolderPath) { using (FolderBrowserDialog folderBrowserDialog =
new FolderBrowserDialog()) {
folderBrowserDialog.Description = OpenFolderTitle;
folderBrowserDialog.ShowNewFolderButton = true;
folderBrowserDialog.SelectedPath = defaultFolderPath;
folderBrowserDialog.ShowDialog();
return folderBrowserDialog.SelectedPath;
}
}
}
}
We start with our using directives and see an example of a using alias directive. In this case, we don't want to add a normal using directive for the System.Windows.Forms assembly because it contains many UI-related classes that have names that clash with those in the required System.Windows assembly.
To avoid these conflicts, we can create an alias for the single type that we are interested in using from that assembly. To clarify, Microsoft decided not to reinvent the wheel, or, in this case, the FolderBrowserDialog control, in the System.Windows assembly, and so we need to add a reference to the System.Windows.Forms assembly and use the one from there.
Looking at this class, we see that much of this code is taken up with the declarations of the Dependency Properties of the control. We have the FolderPath property that will hold the file path of the folder that is selected from the Windows.Forms control, and the OpenFolderTitle property that will populate the title bar of the FolderBrowserDialog window when displayed.
Next, we see the TextBox_PreviewMouseLeftButtonUp event handler that handles the PreviewMouseLeftButtonUp event of the single TextBox element in our control. In this method, we first verify that the user is not selecting text from, or scrolling, the TextBox control and then, if true, we call the ShowFolderPathEditWindow method.
In order to verify that the user is not selecting text, we simply check the length of the SelectedText property of the TextBox control. In order to confirm that the user is not scrolling the TextBox control, we compare the relative horizontal position of the user's click with the length of the TextBox element minus the width of its vertical scroll bar to ensure that their mouse is not over the scroll bar, if present.
The ShowFolderPathEditWindow method first prepares to display the Windows.Forms control. It sets the defaultFolderPath variable to either the current value of the FolderPath property, if one is set, or the current user's Documents folder, using the Environment.GetFolderPath method and the Environment.SpecialFolder.MyDocuments enumeration.
It then calls the ShowFolderBrowserDialog method to launch the actual FolderBrowserDialog control and retrieve the selected folder path. If a valid folder path is selected, we set its value to the data bound FolderPath property directly, but note that we could have set it in other ways.
It would be very easy to add an ICommand property to our control in order to return the selected folder path instead of using this direct assignment. This could be useful in cases where we don't want the data bound value to be set instantly; for example, if the control was used in a child window that needed a confirmation button to be clicked before the data bound value could be updated.
The ShowFolderBrowserDialog method wraps the use of the FolderBrowserDialog class in a using statement, to ensure that it is disposed of, once it has been used. It utilizes the defaultFolderPath variable and the OpenFolderTitle property when setting up the actual FolderBrowserDialog control. Note that this OpenFolderTitle property is simply here to demonstrate how we can expose the required properties from the FolderBrowserDialog element in our control. In this way, we can encapsulate the use of the Windows.Forms control and assembly within our control.
Note that we could have added extra Dependency Properties to enable the users of our framework to have further control over the settings in the FolderBrowserDialog control. In this basic example, we simply hardcoded a positive value for the FolderBrowserDialog.ShowNewFolderButton property, but we could have exposed that as another property.
We could have also added a browse button and maybe even a clear button to clear the selected folder value. We could have then added additional bool Dependency Properties to control whether those buttons should be displayed or not. There are many other ways that we could improve this control, but it still demonstrates how we can encapsulate functionality into our Views components. We'll see another View-related way to capture little snippets of functionality in the following section.
- 極簡算法史:從數(shù)學到機器的故事
- PyTorch自然語言處理入門與實戰(zhàn)
- Object-Oriented JavaScript(Second Edition)
- 重學Java設計模式
- Mastering Android Development with Kotlin
- C#應用程序設計教程
- UI設計全書(全彩)
- Visual Basic程序設計實驗指導及考試指南
- Using Yocto Project with BeagleBone Black
- 微信小程序開發(fā)邊做邊學(微課視頻版)
- Responsive Web Design with jQuery
- Jenkins 2.x實踐指南
- 大象:Thinking in UML(第二版)
- OpenStack Sahara Essentials
- OpenCV:Computer Vision Projects with Python