简体   繁体   English

WPF自定义控件,拆分字符串的最佳方法

[英]WPF Custom Control, best way to split string

I have got a custom property which will have a name entered into it. 我有一个自定义属性,将在其中输入名称。 As you can see below: 如下所示:

UserProfile.cs UserProfile.cs

using System;
using System.ComponentModel;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;

namespace Controls
{
    public class UserProfile : Control
    {
        #region Fields
        public static readonly DependencyProperty PhotoSizeProperty;
        public static readonly DependencyProperty UserNameProperty;
        #endregion

        static UserProfile()
        {
            // Initialize as lookless control
            DefaultStyleKeyProperty.OverrideMetadata(typeof(UserProfile),
                new FrameworkPropertyMetadata(typeof(UserProfile)));

            // Initialize dependency properties
            PhotoSizeProperty = DependencyProperty.Register("PhotoSize", typeof(Double), typeof(UserProfile), null);
            UserNameProperty = DependencyProperty.Register("UserName", typeof(String), typeof(UserProfile), null);
        }

        #region Custom Control Properties
        /// <summary>
        /// Gets or sets the Label which is displayed next to the field
        /// </summary>
        [Description("Size of the user image"), Category("Common Properties")]
        public Double PhotoSize
        {
            get { return (Double)GetValue(PhotoSizeProperty); }
            set { SetValue(PhotoSizeProperty, value); }
        }

        /// <summary>
        /// Gets or sets the Label which is displayed next to the field
        /// </summary>
        [Description("Username, split first and last names"), Category("Common Properties")]
        public String UserName
        {
            get { return (String)GetValue(UserNameProperty); }
            set { SetValue(UserNameProperty, value); }
        }
        #endregion
    }
    public class CalculateBorder : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Double parm = System.Convert.ToDouble(parameter);
            return new Thickness((double)value / parm);
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    public class CalculateFont : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            Double parm = System.Convert.ToDouble(parameter);
            return (double)value / parm;
        }
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

This is my Generic style for my control. 这是我控制的通用样式。 There are two TextBlock's, one needs to have the first name and the second should have the second name. 有两个TextBlock,一个需要使用名字,第二个需要使用名字。 What's the best way to split this name given for this without too much over the top code? 拆分此名称的最佳方法是什么,而不必在顶层代码中过多?

Generic.xaml 泛型

<Style TargetType="{x:Type local:UserProfile}">
        <Setter Property="Width" Value="150" />
        <Setter Property="Foreground" Value="White" />
        <Setter Property="FontSize" Value="{Binding Converter={StaticResource CalculateFont}, 
            ConverterParameter=35, 
            RelativeSource={RelativeSource AncestorType={x:Type local:UserProfile}}, 
            Path=(local:UserProfile.PhotoSize)}" />
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type local:UserProfile}">
                    <Grid x:Name="circleGrid" Width="{Binding PhotoSize}">
                        <Grid.RowDefinitions>
                            <RowDefinition Height="{Binding Path=ActualWidth, ElementName=circleGrid}" />
                            <RowDefinition />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <Border x:Name="circleBorder"
                            Grid.Row="0"
                            CornerRadius="{Binding Path=ActualWidth, ElementName=circleGrid}"
                            Width="{Binding Path=ActualWidth, ElementName=circleGrid}"
                            Height="{Binding Path=ActualWidth, ElementName=circleGrid}"
                            BorderBrush="White"
                            BorderThickness="{Binding Converter={StaticResource CalculateBorder}, 
                        ConverterParameter=35, 
                        RelativeSource={RelativeSource AncestorType={x:Type local:UserProfile}}, 
                        Path=(local:UserProfile.PhotoSize)}">
                            <Border.Background>
                                <ImageBrush ImageSource="D:\Users\Martyn Ball\Pictures\11061728_10153409797331063_2946862347621203654_o.jpg" Stretch="UniformToFill" />
                            </Border.Background>
                        </Border>

                        <WrapPanel Grid.Row="1" HorizontalAlignment="Center">
                            <TextBlock x:Name="firstName"
                                       FontWeight="Bold"
                                       Foreground="{TemplateBinding Foreground}"
                                       Text="{TemplateBinding UserName}" />
                            <TextBlock Text=" "/>
                            <TextBlock x:Name="lastName"
                                       FontWeight="Normal"
                                       Foreground="{TemplateBinding Foreground}"
                                       Text="{TemplateBinding UserName}" />
                        </WrapPanel>
                    </Grid>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

Edit: 编辑:

Why can't I just create this property like so, SplitUsername ? 为什么我不能像SplitUsername这样创建此属性?

/// <summary>
        /// Gets or sets the Label which is displayed next to the field
        /// </summary>
        [Description("Username, split first and last names"), Category("Common Properties")]
        public String UserName
        {
            get { return (String)GetValue(UserNameProperty); }
            set { SetValue(UserNameProperty, value); }
        }
        public string[] SplitUsername
        {
            get { return GetValue(UserNameProperty).ToString().Split(' '); }
        }
        #endregion

I'm trying to Bind to it in my style, and I get an error saying this it isn't recognized or accessible. 我正在尝试以我的风格绑定到它,但是收到一条错误消息,提示它无法识别或无法访问。

Text="{TemplateBinding SplitUsername[0]}"

Edit 2 编辑2

Okay, so this is the property which should make an array containing [0] => "Firstname, [1] => "Secondname" . 好的,因此这是一个属性,该属性应构成一个包含[0] => "Firstname, [1] => "Secondname"

public string[] SplitUsername
{
    get { return GetValue(UserNameProperty).ToString().Split(' '); }
}

And here is my binding, which isn't working: 这是我的绑定,不起作用:

<TextBlock x:Name="firstName"
            FontWeight="Bold"
            Foreground="{TemplateBinding Foreground}"
            Text="{Binding SplitUsername[0]}" />
<TextBlock Text=" "/>
<TextBlock x:Name="lastName"
            FontWeight="Normal"
            Foreground="{TemplateBinding Foreground}"
            Text="{Binding SplitUsername[1]}" />

I don't seem to be getting any errors! 我似乎没有任何错误!

Edit 3 编辑3

/// <summary>
        /// Gets or sets the Label which is displayed next to the field
        /// </summary>
        [Description("Username, split first and last names"), Category("Common Properties")]
        public String UserName
        {
            get { return (String)GetValue(UserNameProperty); }
            set { SetValue(UserNameProperty, value); }
        }
        public static readonly DependencyProperty UserNameProperty = DependencyProperty.Register("UserName", typeof(String), typeof(UserProfile), 
                new FrameworkPropertyMetadata(
                    false,
                    new PropertyChangedCallback(UserNamePropertyChanged)));

        private static void UserNamePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
        {
            UserProfile profile = sender as UserProfile;

            TextBlock firstName = profile.GetTemplateChild("firstName") as TextBlock;
            TextBlock lastName = profile.GetTemplateChild("lastName") as TextBlock;

            if (firstName != null && lastName != null)
            {
                if (args.NewValue == null)
                {
                    firstName.Text = string.Empty;
                    lastName.Text = string.Empty;
                }
                else
                {
                    string newValue = args.NewValue.ToString();

                    if (string.IsNullOrWhiteSpace(newValue))
                    {
                        firstName.Text = string.Empty;
                        lastName.Text = string.Empty;
                    }
                    else
                    {
                        string[] splittedValues = newValue.Split(' ');

                        if (splittedValues.Length == 1)
                        {
                            firstName.Text = newValue;
                            lastName.Text = string.Empty;
                        }
                        else if (splittedValues.Length == 2)
                        {
                            firstName.Text = splittedValues[0];
                            lastName.Text = splittedValues[1];
                        }
                        else if (splittedValues.Length > 2)
                        {
                            firstName.Text = splittedValues[0];
                            lastName.Text = newValue.Substring(splittedValues[0].Length + 1);
                        }
                    }
                }
            }
        }
        #endregion

Use Control's Tag property. 使用控件的标签属性。 Split the full name using Split() and store resulting array in Tag property. 使用Split()拆分全名,并将结果数组存储在Tag属性中。

Eg; 例如;

<Button x:Name="BtnName" Content="Anjum Khan" />
<TextBlock Background="#FFEECF0A" Text="{Binding Tag[0], ElementName=BtnName}" />
<TextBlock Background="#FF5DF1AE"  Text="{Binding Tag[1], ElementName=BtnName}"  />


BtnName.Tag = BtnName.Content.ToString().Split(new char[] { ' ' });

TextBlocks will show First and Last name respectively. TextBlocks将分别显示名字和姓氏。 You can build upon this concept. 您可以基于此概念。

Your templatebindings will work too : 您的模板绑定也将起作用:

<TextBlock x:Name="firstName"
           FontWeight="Bold"
           Foreground="{TemplateBinding Foreground}"
           Text="{TemplateBinding Tag[0]}" />

Instead of binding from the control template I would suggest you react on the changed UserName property and set the textboxes in code. 建议您不要对控件模板进行绑定,而应对更改后的UserName属性做出反应,并在代码中设置文本框。 Like this: 像这样:

    /// <summary>
    /// Gets or sets the Label which is displayed next to the field
    /// </summary>
    [Description("Username, split first and last names"), Category("Common Properties")]
    public String UserName
    {
        get { return (String)GetValue(UserNameProperty); }
        set { SetValue(UserNameProperty, value); }
    }

    public static readonly DependencyProperty UserNameProperty = DependencyProperty.Register("UserName", typeof(String), typeof(UserProfile), new PropertyMetadata("Firstname Lastname",UserNamePropertyChanged));

    private static void UserNamePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        UserProfile profile = sender as UserProfile;
        profile.RefreshFirstAndLastName();            
    }

    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();
        this.RefreshFirstAndLastName();
    }

    private void RefreshFirstAndLastName()
    {
        TextBlock firstName = this.GetTemplateChild("firstName") as TextBlock;
        TextBlock lastName = this.GetTemplateChild("lastName") as TextBlock;

        if (firstName != null && lastName != null)
        {
            if (string.IsNullOrWhiteSpace(this.UserName))
            {
                firstName.Text = string.Empty;
                lastName.Text = string.Empty;
            }
            else
            {
                string[] splittedValues = this.UserName.Split(' ');

                if (splittedValues.Length == 1)
                {
                    firstName.Text = this.UserName;
                    lastName.Text = string.Empty;
                }
                else if (splittedValues.Length == 2)
                {
                    firstName.Text = splittedValues[0];
                    lastName.Text = splittedValues[1];
                }
                else if (splittedValues.Length > 2)
                {
                    firstName.Text = splittedValues[0];
                    lastName.Text = this.UserName.Substring(splittedValues[0].Length + 1);
                }
            }
        }
    }

I know this looks odd when working with WPF. 我知道在使用WPF时,这看起来很奇怪。 But keep in mind you are within a control . 但是请记住,您处于控制范围之内 You are inside a view and this doesn´t violate MVVM. 您在视图中,这并不违反MVVM。

Update 1: 更新1:

I found the issue with your code. 我发现您的代码有问题。 Change the definition of the UserProfile in MainWindow.xaml like this: 像这样在MainWindow.xaml中更改UserProfile的定义:

<Controls:UserProfile PhotoSize="150" UserName="{Binding Text, ElementName=Username}" />

You have to bind to the Text property of the TextBox. 您必须绑定到TextBox的Text属性。

Managed to get it working, this is the binding in Generic.xaml 设法使其正常工作,这是Generic.xaml中的绑定

<TextBlock x:Name="firstName"
            FontWeight="Bold"
            Foreground="{TemplateBinding Foreground}"
            Text="{Binding SplitUsername[0], RelativeSource={RelativeSource TemplatedParent}}" />
<TextBlock Text=" "/>
<TextBlock x:Name="lastName"
            FontWeight="Normal"
            Foreground="{TemplateBinding Foreground}"
            Text="{Binding SplitUsername[1], RelativeSource={RelativeSource TemplatedParent}}" />

This is the code behind in UserProfile.cs 这是UserProfile.cs中的代码

#region Custom Control Properties
/// <summary>
/// Gets or sets the Label which is displayed next to the field
/// </summary>
[Description("Size of the user image"), Category("Common Properties")]
public Double PhotoSize
{
    get { return (Double)GetValue(PhotoSizeProperty); }
    set { SetValue(PhotoSizeProperty, value); }
}

/// <summary>
/// Gets or sets the Label which is displayed next to the field
/// </summary>
[Description("Username, split first and last names"), Category("Common Properties")]
public String UserName
{
    get { return (String)GetValue(UserNameProperty); }
    set { SetValue(UserNameProperty, value); }
}
public string[] SplitUsername
{
    get { return GetValue(UserNameProperty).ToString().Split(' '); }
}
#endregion

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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