简体   繁体   中英

Bindings inside WPF user control

In order to get into the WPF world and getting used to bindings, I've made a user control used to define a search filter. Depending on the wanted filter, the user can either enter a text, pick a date or select an item in a combo box. Here's an example with three instances of the created search control, each being of different type:

搜索过滤器示例

The good news is, everything is working but I'm not sure if everything has been done as intended.

SearchUserControl.xaml:

<UserControl x:Class="Zefix.View.UserControls.SearchUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" 
             d:DesignHeight="82" 
             d:DesignWidth="300" 
             Height="Auto"
             x:Name="SearchUserControlRoot">
    <Grid>
        <StackPanel>
            <Label Name="LabelHeaderText" Content="{Binding HeaderText, ElementName=SearchUserControlRoot}" />
            <TextBox Name="TextBoxSearchText" Text="{Binding SearchValue, ElementName=SearchUserControlRoot}" Visibility="{Binding TextBoxVisiblity, ElementName=SearchUserControlRoot}" />
            <DatePicker Name="DatePickerSearch" SelectedDate="{Binding SearchValue, ElementName=SearchUserControlRoot}" Visibility="{Binding DatePickerVisiblity, ElementName=SearchUserControlRoot}" />
            <ComboBox Name="ComboBoxSearch" Text="{Binding SearchValue, ElementName=SearchUserControlRoot}" ItemsSource="{Binding AvailableValues, ElementName=SearchUserControlRoot}" Visibility="{Binding ComboBoxVisiblity, ElementName=SearchUserControlRoot}" IsEditable="True" />
        </StackPanel>
    </Grid>
</UserControl>

SearchUserControl.xaml.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows;
using Zefix.DataAccess;

namespace Zefix.View.UserControls {
    /// <summary>
    ///     Interaction logic for SearchUserControl.xaml
    /// </summary>
    public partial class SearchUserControl {

        #region Public Dependency Properties

        /// <summary>
        /// The search value property
        /// </summary>
        public static readonly DependencyProperty SearchValueProperty =
            DependencyProperty.Register("SearchValue", typeof (object), typeof (SearchUserControl));

        /// <summary>
        /// The available values property
        /// </summary>
        public static readonly DependencyProperty AvailableValuesProperty =
            DependencyProperty.Register("AvailableValues", typeof (IEnumerable<object>), typeof (SearchUserControl));

        /// <summary>
        /// The search type property
        /// </summary>
        public static readonly DependencyProperty SearchTypeProperty =
            DependencyProperty.Register("SearchType", typeof (SearchType), typeof (SearchUserControl));

        /// <summary>
        /// The header text property
        /// </summary>
        public static readonly DependencyProperty HeaderTextProperty =
            DependencyProperty.Register("HeaderText", typeof (string), typeof (SearchUserControl));

        #endregion

        #region Private Dependency Properties

        /// <summary>
        /// The combo box visiblity property
        /// </summary>
        private static readonly DependencyProperty ComboBoxVisiblityProperty =
            DependencyProperty.Register("ComboBoxVisiblity", typeof (Visibility), typeof (SearchUserControl));

        /// <summary>
        /// The text box visiblity property
        /// </summary>
        private static readonly DependencyProperty TextBoxVisiblityProperty =
            DependencyProperty.Register("TextBoxVisiblity", typeof (Visibility), typeof (SearchUserControl));

        /// <summary>
        /// The date picker visiblity property
        /// </summary>
        private static readonly DependencyProperty DatePickerVisiblityProperty =
            DependencyProperty.Register("DatePickerVisiblity", typeof (Visibility), typeof (SearchUserControl));

        #endregion

        #region Public Properties

        /// <summary>
        ///     Gets or sets the type of the search.
        /// </summary>
        /// <value>
        ///     The type of the search.
        /// </value>
        public SearchType SearchType {
            get { return (SearchType) GetValue(SearchTypeProperty); }
            set { SetValue(SearchTypeProperty, value); }
        }

        /// <summary>
        ///     Gets or sets the header text.
        /// </summary>
        /// <value>
        ///     The header text.
        /// </value>
        public string HeaderText {
            get { return (string) GetValue(HeaderTextProperty); }
            set { SetValue(HeaderTextProperty, value); }
        }

        /// <summary>
        /// Gets or sets the available values.
        /// </summary>
        /// <value>
        /// The available values.
        /// </value>
        public IEnumerable<object> AvailableValues {
            get { return (IEnumerable<object>) GetValue(AvailableValuesProperty); }
            set { SetValue(AvailableValuesProperty, value); }
        }

        /// <summary>
        /// Gets or sets the search value.
        /// </summary>
        /// <value>
        /// The search value.
        /// </value>
        public object SearchValue {
            get { return GetValue(SearchValueProperty); }
            set { SetValue(SearchValueProperty, value); }
        }

        #endregion

        #region Private Properties

        /// <summary>
        /// Gets or sets the combo box visiblity.
        /// </summary>
        /// <value>
        /// The combo box visiblity.
        /// </value>
        private Visibility ComboBoxVisiblity {
            get { return (Visibility) GetValue(ComboBoxVisiblityProperty); }
            set { SetValue(ComboBoxVisiblityProperty, value); }
        }

        /// <summary>
        /// Gets or sets the date picker visiblity.
        /// </summary>
        /// <value>
        /// The date picker visiblity.
        /// </value>
        private Visibility DatePickerVisiblity {
            get { return (Visibility) GetValue(DatePickerVisiblityProperty); }
            set { SetValue(DatePickerVisiblityProperty, value); }
        }

        /// <summary>
        /// Gets or sets the text box visiblity.
        /// </summary>
        /// <value>
        /// The text box visiblity.
        /// </value>
        private Visibility TextBoxVisiblity {
            get { return (Visibility) GetValue(TextBoxVisiblityProperty); }
            set { SetValue(TextBoxVisiblityProperty, value); }
        }

        #endregion

        #region Constructor

        /// <summary>
        ///     Initializes a new instance of the <see cref="SearchUserControl" /> class.
        /// </summary>
        public SearchUserControl() {
            InitializeComponent();

            DependencyPropertyDescriptor pd = DependencyPropertyDescriptor.FromProperty(SearchTypeProperty, typeof (SearchUserControl));
            pd.AddValueChanged(this, OnSearchTypePropertyChanged);

            // Initialize default parameters
            SearchType = SearchType.Unknown;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Called when the search type property has changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="EventArgs"/> instance containing the event data.</param>
        private void OnSearchTypePropertyChanged(object sender, EventArgs e) {

            // Hide all editors
            DatePickerVisiblity = Visibility.Collapsed;
            ComboBoxVisiblity = Visibility.Collapsed;
            TextBoxVisiblity = Visibility.Collapsed;

            // Make the correct editor visible
            switch (SearchType) {
                case SearchType.Date:
                    DatePickerVisiblity = Visibility.Visible;
                    break;
                case SearchType.TextSelection:
                    ComboBoxVisiblity = Visibility.Visible;
                    break;
                case SearchType.Text:
                    TextBoxVisiblity = Visibility.Visible;
                    break;
            }
        }

        #endregion
    }
}

Instantiation of the search controls from the parent control:

        <ribbon:Tab Label="Search">
            <ribbon:Group Padding="0,5,0,5">
                <customcontrols:SearchUserControl x:Name="SearchUserControlCompanyName" HeaderText="company name" Margin="5,0,0,0" SearchType="Text" VerticalAlignment="Center" VerticalContentAlignment="Center" />
                <customcontrols:SearchUserControl x:Name="SearchUserControlCompanyNationality" HeaderText="company nationality (ISO3 code)" Margin="5,0,0,0" SearchType="TextSelection" AvailableValues="{Binding Path=CompaniesViewModel.ISO3Codes}" VerticalAlignment="Center" />
                <customcontrols:SearchUserControl x:Name="SearchUserControlDateFounded" HeaderText="date founded" Margin="5,0,0,0" SearchType="Date" VerticalAlignment="Center" VerticalContentAlignment="Center" />
                <ribbon:Button Context="StatusBarItem" Name="ButtonApplyFilter" Label="Search" ImageSourceSmall="/Resources/search_magnifying_glass_find.png" Margin="5,0,0,0" VerticalAlignment="Center" Click="OnButtonApplyFilterClicked" Command="{Binding Path=ApplyFilterCommand}" ScreenTipHeader="Apply the search filter" VerticalContentAlignment="Center" VariantSize="Large" />
            </ribbon:Group>
        </ribbon:Tab>

In the SearchControl I wanted to display the correct component (textbox, datepicker or combobox) according to the set SearchType. For this the, xxxVisibility dependency properties and properties have been created (they are being set when the SearchTypeProperty notifies a property changed event). As there is no reason to expose them as public (they are being used only inside the SearchControl), I've made them private; MSDN states that bound properties MUST be public though. The project compiles and runs without an issue, but errors are being shown for the bound xxxVisibility properties with the message 'Public member expected' (can't tell if it's visual studio or resharper telling me that).

Is my approach to create this user control correct in respect to the WPF concepts? Should the xxxVisibility properties be public (event though I don't want to expose them)?

This is a very difficult question to 'answer', rather than just 'comment' on. In my personal opinion, your UserControl has been written well and as far as I can see doesn't break any rules. Although I don't see any problem with declaring a private DependencyProperty , it is unusual. In this situation, developers often chose to implement a public Read Only DependencyProperty with a private DependencyPropertyKey instead:

private static readonly DependencyPropertyKey ComboBoxVisiblityPropertyKey
    = DependencyProperty.RegisterReadOnly("ComboBoxVisiblity", typeof(int), 
    typeof(SearchUserControl), new PropertyMetadata(Visibility.Collapsed));

public static readonly DependencyProperty ComboBoxVisiblityProperty
    = ComboBoxVisiblityPropertyKey.DependencyProperty;

public int ComboBoxVisiblity
{
    get { return (int)GetValue(ComboBoxVisiblityProperty); }
    protected set { SetValue(ComboBoxVisiblityPropertyKey, value); }
}

Some developers may also think it unusual that you are creating properties of type Visibility rather than binding bool values with BoolToVisibilityConverter s, but again... that is your prerogative. Overall, well done! :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM