WPF自定义控件,分割字符串

时间:2015-11-26 17:47:08

标签: c# wpf xaml

我有一个自定义属性,其中会输入一个名称。如下所示:

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();
        }
    }
}

这是我控制的通用样式。有两个TextBlock,一个需要有名字,第二个应该有第二个名字。在没有太多代码的情况下,为此分配此名称的最佳方法是什么?

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>

修改

为什么我不能像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

我正试图以我的风格绑定它,我收到一个错误,说它无法识别或无法访问。

Text="{TemplateBinding SplitUsername[0]}"

修改2

好的,所以这是应该创建一个包含[0] => "Firstname, [1] => "Secondname"的数组的属性。

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

这是我的绑定,它不起作用:

<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]}" />

我似乎没有收到任何错误!

编辑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

3 个答案:

答案 0 :(得分:1)

使用Control的Tag属性。使用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将分别显示名字和姓氏。你可以建立在这个概念之上。

您的模板绑定也会起作用:

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

答案 1 :(得分:1)

我建议您对已更改的UserName属性做出反应并在代码中设置文本框,而不是从控件模板进行绑定。像这样:

    /// <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);
                }
            }
        }
    }

我知道在使用WPF时这看起来很奇怪。但请记住,您在控件中 。你在视图中,这不违反MVVM。

更新1:

我发现您的代码存在问题。在MainWindow.xaml中更改UserProfile的定义,如下所示:

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

您必须绑定到TextBox的Text属性。

答案 2 :(得分:0)

管理以使其正常工作,这是 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}}" />

这是 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