官术网_书友最值得收藏!

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.

主站蜘蛛池模板: 曲麻莱县| 绥滨县| 鄂托克旗| 宣武区| 彭水| 广河县| 穆棱市| 长春市| 娱乐| 辽源市| 卢龙县| 肇州县| 大荔县| 桃源县| 宁陕县| 吉首市| 方城县| 淅川县| 方正县| 新民市| 增城市| 台前县| 保山市| 个旧市| 呼玛县| 都安| 榆林市| 定兴县| 奉化市| 嘉荫县| 宽城| 庆安县| 成安县| 大姚县| 城市| 新津县| 涿州市| 澳门| 思茅市| 厦门市| 洛隆县|