Silverlight开发中的疑难杂症:如何实现一个EditorBox -电脑资料

电脑资料 时间:2019-01-01 我要投稿
【www.unjs.com - 电脑资料】

EditorBox就是一个具有编辑和展示两种状态的TextBox,因为在最近的工作和学习项目中 ,多次碰到了需要将一个TextBox以编辑和展示两种不同的样式存在,于是就想到了制作一个 这样的控件来提高生产效率,同时也尝试一下自定义控件的开发,

Silverlight开发中的疑难杂症:如何实现一个EditorBox

。该控件包括如下功能:

l 能在编辑和展示状态之间切换;

l 可以设置是否能够编辑;

l 在展示状态双击控件,进入到编辑状态(如果支持编辑);

l 在编辑状态,输入完文本,回车后进入展示状态;

l 提供一个Text属性供外部使用;

l 能够设置展示状态下文本样式(设置指定区间的文本的字体、字体大小、字体颜色等) ;

基本的实现思路是这样的:首先,定义两个TemplatePart,分别为TextBox和TextBlock类 型,用来表示编辑框跟展示框,文本格式的处理通过动态计算所设置的格式,然后添加多个 Run元素来实现;然后,定一个两个TemplateVisualState,用来实现编辑状态和展示状态之 间的切换。附加的Attribute声明如下:

[TemplatePart(Name = "PART_Editor", Type = typeof(TextBox))]

[TemplatePart(Name = "PART_View", Type = typeof(TextBlock))]

[TemplateVisualState(Name = "Edit", GroupName = "CommonStates")]

[TemplateVisualState(Name = "View", GroupName = "CommonStates")]

为了使控件使用者能够对样式进行更好的控制,这里定义了一个TextFormat类,与单个的 样式设置对应,里面包括字体、字体大小、字体颜色、样式应用的起始索引、应用的总长度 ,具体的类实现如下:

TextFormat

/// 


/// 文本格式
/// 

public class TextFormat : DependencyObject
{
     /// 
     /// 字体
     /// 

     public FontFamily FontFamily
     {
         get { return (FontFamily)GetValue(FontFamilyProperty); }
         set { SetValue(FontFamilyProperty, value); }
     }
     public static readonly DependencyProperty FontFamilyProperty  =
         DependencyProperty.Register("FontFamily", typeof(FontFamily),  typeof(TextFormat), new PropertyMetadata(default(FontFamily)));
     /// 
     /// 字体大小
     /// 

     public double FontSize
     {
         get { return (double)GetValue(FontSizeProperty); }
         set { SetValue(FontSizeProperty, value); }
     }
     public static readonly DependencyProperty FontSizeProperty =
         DependencyProperty.Register("FontSize", typeof(double),  typeof(TextFormat), new PropertyMetadata(10.0));
     /// 
     /// 字体颜色
     /// 

     public Brush Foreground
     {
         get { return (Brush)GetValue(ForegroundProperty); }
         set { SetValue(ForegroundProperty, value); }
     }
     public static readonly DependencyProperty ForegroundProperty  =
         DependencyProperty.Register("Foreground", typeof(Brush),  typeof(TextFormat), new PropertyMetadata(new SolidColorBrush (Colors.Black)));
     /// 
     /// 样式应用的起始索引
     /// 

     public int StartIndex
     {
         get { return (int)GetValue(StartIndexProperty); }
         set { SetValue(StartIndexProperty, value); }
     }
     public static readonly DependencyProperty StartIndexProperty  =
         DependencyProperty.Register("StartIndex", typeof(int),  typeof(TextFormat), new PropertyMetadata(0));
     /// 
     /// 样式应用的长度
     /// 

     public int Length
     {
         get { return (int)GetValue(LengthProperty); }
         set { SetValue(LengthProperty, value); }
     }
     public static readonly DependencyProperty LengthProperty =
         DependencyProperty.Register("Length", typeof(int), typeof (TextFormat), new PropertyMetadata(0));
}
/// 
/// 文本格式集合
/// 

public class TextFormatCollection :  ObservableCollection
{
}

之后是依赖属性的定义,除了之前提到过的文本格式集合以及当前选择的模式之外,还包 括对外提供的文本和是否允许编辑选项,同时在文本格式集合以及当前选择的模式改变时进 行文本格式化处理,依赖属性的定义如下:

Dependency Properties

#region Dependency Properties

/// 


/// 文本格式集合
/// 

public TextFormatCollection TextFormats
{
     get { return (TextFormatCollection)GetValue(TextFormatsProperty);  }
     set { SetValue(TextFormatsProperty, value); }
}
public static readonly DependencyProperty TextFormatsProperty =
     DependencyProperty.Register("TextFormats", typeof (TextFormatCollection), typeof(EditorBox), new PropertyMetadata(new  PropertyChangedCallback(OnTextFormatsChanged)));
/// 
/// 文本
/// 

public string Text
{
     get { return (string)GetValue(TextProperty); }
     set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
     DependencyProperty.Register("Text", typeof(string), typeof (EditorBox), new PropertyMetadata(string.Empty));
/// 
/// 是否允许编辑
/// 

public bool CanEdit
{
     get { return (bool)GetValue(CanEditProperty); }
     set { SetValue(CanEditProperty, value); }
}
public static readonly DependencyProperty CanEditProperty =
     DependencyProperty.Register("CanEdit", typeof(bool), typeof (EditorBox), new PropertyMetadata(true));
/// 
/// 当前模式
/// 

public Mode CurrentMode
{
     get { return (Mode)GetValue(CurrentModeProperty); }
     set { SetValue(CurrentModeProperty, value); }
}
public static readonly DependencyProperty CurrentModeProperty =
     DependencyProperty.Register("CurrentMode", typeof(Mode), typeof (EditorBox), new PropertyMetadata(Mode.View, new PropertyChangedCallback (OnCurrentModeChanged)));
#region Property Change Handler
private static void OnTextFormatsChanged(DependencyObject obj,  DependencyPropertyChangedEventArgs e)
{
     (obj as EditorBox).OnTextFormatsChanged(e.OldValue as  TextFormatCollection, e.NewValue as TextFormatCollection);
}
/// 
/// 文本格式设置改变时,重新计算文本格式
/// 

/// 
/// 
protected virtual void OnTextFormatsChanged(TextFormatCollection  oldCollection, TextFormatCollection newCollection)
{
     if (oldCollection != null)
     {
         oldCollection.CollectionChanged -= new  NotifyCollectionChangedEventHandler(TextFormats_CollectionChanged);
     }
     if (newCollection != null)
     {
         newCollection.CollectionChanged += new  NotifyCollectionChangedEventHandler(TextFormats_CollectionChanged);
     }
     //集合改变时重新计算文本格式
     ProcessTextFormat();
}
/// 
/// 集合项改变时,重新计算文本格式
/// 

/// 
/// 
void TextFormats_CollectionChanged(object sender,  System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
     ProcessTextFormat();
}
private static void OnCurrentModeChanged(DependencyObject obj,  DependencyPropertyChangedEventArgs e)
{
     (obj as EditorBox).OnCurrentModeChanged((Mode)e.OldValue, (Mode) e.NewValue);
}
/// 
/// 从编辑模式切换到视图模式,进行文本格式计算
/// 

/// 
/// 
protected virtual void OnCurrentModeChanged(Mode oldMode, Mode newMode)
{
     if (newMode == Mode.View)
     {
         ProcessTextFormat();
     }
}
#endregion

由于使用了TemplatePart定义实现了界面与控件行为逻辑之间的解耦,那么自然的需要在 运行时拿到在样式中所定义的TemplatePart。这里通过重载OnApplyTemplate方法来实现子控 件的查找,以及相应的处理事件的附加,实现代码如下:

OnApplyTemplate

public override void OnApplyTemplate()
{
     base.OnApplyTemplate();
     AttachToVisualTree();
}
/// 


/// 获取模板中的子控件,并附加处理
/// 

void AttachToVisualTree()
{
     //获取模板中的子控件
     _editor = GetChildControl(PART_Editor);
     _viewer = GetChildControl(PART_View);
     if (_editor != null)
     {
         //由于Silverlight的TextChanged事件只在Load之后才会触发,所以需 要在Load之后初始化文本格式
         _editor.Loaded += new RoutedEventHandler (InitTextFormat);
         //按下回车回到视图模式
         _editor.KeyDown += new KeyEventHandler(EnterViewMode);
         //设置绑定关系
         _editor.Text = this.Text;
         this.SetBinding(TextProperty, new Binding("Text") { Source  = _editor, Mode = BindingMode.TwoWay });
     }
     ProcessTextFormat();
}

在实际测试时,这里发现了一个问题,当我在上面的方法中设置TextBox的Text属性后, 对应控件中注册的TextChanged事件并没有触发,经过多次的调试,发现似乎只有在控件Load 完之后进行的Text属性赋值操作,才会引起TextChanged事件;然而测试了WPF中的TextBox, 并没有发现有一样的问题,在网上也没有发现有类似的讨论,只好作罢。最后通过注册 TextBox的Loaded事件,并在里面重新进行了文本格式的处理。如果有对这个问题有所了解的 朋友,希望能够给我答疑解惑~

接下来是最重要的文本格式的处理,这部分的具体思路是这样的:

1.判断是否处于展示模式;

2.清楚原有的Inlines集合;

3.将TextFormats集合中的元素按照StartIndex从小到大进行排序;

4.循环处理TextFormats集合中的元素;

5.如果当前格式覆盖了前面的格式(StartIndex>LastIndex),则抛出异常;

6.如果当前格式与前面的格式之间有空隙,则将空隙单独设置为默认格式;

7.按照当前格式进行设置;

8.循环结束,如果还有剩余的文本,则全部用默认格式处理。

最后附上完整的代码以及默认的控件样式:

EditorBox

[TemplatePart(Name = "PART_Editor", Type = typeof(TextBox))]
[TemplatePart(Name = "PART_View", Type = typeof(TextBlock))]
[TemplateVisualState(Name = "Edit", GroupName = "CommonStates")]
[TemplateVisualState(Name = "View", GroupName = "CommonStates")]
public class EditorBox : Control
{
     public const string PART_Editor = "PART_Editor";
     public const string PART_View = "PART_View";
     public const string VisualState_Edit = "Edit";
     public const string VisualState_View = "View";
     /// 


     /// 模式
     /// 

     public enum Mode
     {
         /// 
         /// 查看模式
         /// 

         View,
         /// 
         /// 编辑模式
         /// 

         Edit
     }
     #region Private Fields
     private TextBox _editor;
     private TextBlock _viewer;
     #endregion
     #region Dependency Properties
     /// 
     /// 文本格式集合
     /// 

     public TextFormatCollection TextFormats
     {
         get { return (TextFormatCollection)GetValue (TextFormatsProperty); }
         set { SetValue(TextFormatsProperty, value); }
     }
     public static readonly DependencyProperty TextFormatsProperty  =
         DependencyProperty.Register("TextFormats", typeof (TextFormatCollection), typeof(EditorBox), new PropertyMetadata(new  PropertyChangedCallback(OnTextFormatsChanged)));
     /// 
     /// 文本
     /// 

     public string Text
     {
         get { return (string)GetValue(TextProperty); }
         set { SetValue(TextProperty, value); }
     }
     public static readonly DependencyProperty TextProperty =
         DependencyProperty.Register("Text", typeof(string), typeof (EditorBox), new PropertyMetadata(string.Empty));
     /// 
     /// 是否允许编辑
     /// 

     public bool CanEdit
     {
         get { return (bool)GetValue(CanEditProperty); }
         set { SetValue(CanEditProperty, value); }
     }
     public static readonly DependencyProperty CanEditProperty =
         DependencyProperty.Register("CanEdit", typeof(bool), typeof (EditorBox), new PropertyMetadata(true));
     /// 
     /// 当前模式
     /// 

     public Mode CurrentMode
     {
         get { return (Mode)GetValue(CurrentModeProperty); }
         set { SetValue(CurrentModeProperty, value); }
     }
     public static readonly DependencyProperty CurrentModeProperty  =
         DependencyProperty.Register("CurrentMode", typeof(Mode),  typeof(EditorBox), new PropertyMetadata(Mode.View, new  PropertyChangedCallback(OnCurrentModeChanged)));
     #region Property Change Handler
     private static void OnTextFormatsChanged(DependencyObject obj,  DependencyPropertyChangedEventArgs e)
     {
         (obj as EditorBox).OnTextFormatsChanged(e.OldValue as  TextFormatCollection, e.NewValue as TextFormatCollection);
     }
     /// 
     /// 文本格式设置改变时,重新计算文本格式
     /// 

     /// 
     /// 
     protected virtual void OnTextFormatsChanged(TextFormatCollection  oldCollection, TextFormatCollection newCollection)
     {
         if (oldCollection != null)
         {
             oldCollection.CollectionChanged -= new  NotifyCollectionChangedEventHandler(TextFormats_CollectionChanged);
         }
         if (newCollection != null)
         {
             newCollection.CollectionChanged += new  NotifyCollectionChangedEventHandler(TextFormats_CollectionChanged);
         }
         //集合改变时重新计算文本格式
         ProcessTextFormat();
     }
     /// 
     /// 集合项改变时,重新计算文本格式
     /// 

     /// 
     /// 
     void TextFormats_CollectionChanged(object sender,  System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
     {
         ProcessTextFormat();
     }
     private static void OnCurrentModeChanged(DependencyObject obj,  DependencyPropertyChangedEventArgs e)
     {
         (obj as EditorBox).OnCurrentModeChanged((Mode)e.OldValue,  (Mode)e.NewValue);
     }
     /// 
     /// 从编辑模式切换到视图模式,进行文本格式计算
     /// 

     /// 
     /// 
     protected virtual void OnCurrentModeChanged(Mode oldMode, Mode  newMode)
     {
         if (newMode == Mode.View)
         {
             ProcessTextFormat();
         }
     }
     #endregion
     #endregion
     public EditorBox()
     {
         this.DefaultStyleKey = typeof(EditorBox);
         TextFormats = new TextFormatCollection();
         //通过附加属性增加鼠标双击事件
         this.SetValue(MouseEventHelper.MouseDoubleClickProperty, new  MouseButtonEventHandler(MouseDoubleClick));
     }
     public override void OnApplyTemplate()
     {
         base.OnApplyTemplate();
         AttachToVisualTree();
     }
     /// 
     /// 获取模板中的子控件,并附加处理
     /// 

     void AttachToVisualTree()
     {
         //获取模板中的子控件
         _editor = GetChildControl(PART_Editor);
         _viewer = GetChildControl(PART_View);
         if (_editor != null)
         {
             //由于Silverlight的TextChanged事件只在Load之后才会触发 ,所以需要在Load之后初始化文本格式
             _editor.Loaded += new RoutedEventHandler (InitTextFormat);
             //按下回车回到视图模式
             _editor.KeyDown += new KeyEventHandler (EnterViewMode);
             //设置绑定关系
             _editor.Text = this.Text;
             this.SetBinding(TextProperty, new Binding("Text") {  Source = _editor, Mode = BindingMode.TwoWay });
         }
         ProcessTextFormat();
     }
     /// 
     /// 第一次加载时,初始化文本格式
     /// 

     /// 
     /// 
     void InitTextFormat(object sender, RoutedEventArgs e)
     {
         ProcessTextFormat();
     }
     /// 
     /// 进入视图模式
     /// 

     /// 
     /// 
     void EnterViewMode(object sender, KeyEventArgs e)
     {
         //按回车进入查看状态
         if (e.Key == Key.Enter)
         {
             VisualStateManager.GoToState(this, VisualState_View,  false);
             CurrentMode = Mode.View;
         }
     }
     /// 
     /// 双击进入编辑模式(如果允许编辑)
     /// 

     /// 
     /// 
     void MouseDoubleClick(object sender, MouseButtonEventArgs e)
     {
         //更换VisualStatus  双击进入编辑状态
         if (CanEdit)
         {
             VisualStateManager.GoToState(this, VisualState_Edit,  false);
             CurrentMode = Mode.Edit;
         }
     }
     /// 
     /// 处理文本格式
     /// 

     void ProcessTextFormat()
     {
         if (_viewer != null && CurrentMode == Mode.View  && this.TextFormats != null)
         {
             _viewer.Inlines.Clear();
             //先按照StartIndex排序
             var formats = this.TextFormats.OrderBy((format)  => { return format.StartIndex; }).ToList();
             int startIndex = 0;
             int length = 0;
             for (int i = 0; i < formats.Count; i++)
             {
                 if (startIndex >= this.Text.Length)
                     break;
                 TextFormat format = formats[i];
                 Run run = new Run();
                 //重叠部分
                 if (format.StartIndex < startIndex)
                 {
                     throw new Exception("StartIndex不能 重叠");
                 }
                 //不要求格式部分
                 else if (format.StartIndex >  startIndex)
                 {
                     length = Math.Min(format.StartIndex  - startIndex, this.Text.Length - startIndex);
                     run.Text = this.Text.Substring (startIndex, length);
                     startIndex += length;
                     i--;
                 }
                 //要求格式部分
                 else if (format.StartIndex == startIndex)
                 {
                     length = Math.Min(format.Length,  this.Text.Length - startIndex);
                     run.Text = this.Text.Substring (startIndex, length);
                     if (format.FontFamily != null)
                         run.FontFamily =  format.FontFamily;
                     run.FontSize = format.FontSize;
                     run.Foreground =  format.Foreground;
                     startIndex += length;
                 }
                 _viewer.Inlines.Add(run);
             }
             //处理尾部的剩余部分
             if (startIndex < this.Text.Length)
             {
                 Run run = new Run();
                 length = this.Text.Length -  startIndex;
                 run.Text = this.Text.Substring(startIndex,  length);
                 _viewer.Inlines.Add(run);
             }
         }
     }
     /// 
     /// 获取指定名字的控件,并转换为对应类型
     /// 

     /// 控件类型
     /// 控件名
     /// 转换后的控件
     protected T GetChildControl(string ctrlName) where T :  class
     {
         T ctrl = GetTemplateChild(ctrlName) as T;
         return ctrl;
     }
}

默认样式

     xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     xmlns:local="clr-namespace:YQL.CustomControlLibs"  xmlns:d="http://schemas.microsoft.com/expression/blend/2008"  xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"  mc:Ignorable="d" >
    

效果预览:

http://www.bbniu.com/matrix/ShowApplication.aspx?id=70

最新文章