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

关键词: 编辑 展示 控件 开发

Silverlight开发中的疑难杂症:如何实现一个EditorBox(共1篇)

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

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

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=“schemas.microsoft.com/winfx//xaml/presentation”

xmlns:x=“schemas.microsoft.com/winfx/2006/xaml”

xmlns:local=“clr-namespace:YQL.CustomControlLibs” xmlns:d=“schemas.microsoft.com/expression/blend/” xmlns:mc=“schemas.openxmlformats.org/markup-compatibility/2006” mc:Ignorable=“d” >

效果预览:

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

注:本文为网友上传,旨在传播知识,不代表本站观点,与本站立场无关。若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:66553826@qq.com

上一篇:假期观看千与千寻心得感想 下一篇:房地产开发企业资质申报审批行政许可指南及申办流程