65.9K
CodeProject 正在变化。 阅读更多。
Home

Path List Box 冒险 - 第 1 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (4投票s)

2011年9月18日

CPOL

7分钟阅读

viewsIcon

35804

downloadIcon

1713

本文介绍了一个使用Silverlight Pathlistbox控件设计的抽奖轮盘。

目录

引言

去年我曾在CodeProject上发表过一篇关于使用自定义控件的Silverlight PrizeWheel的文章 使用自定义圆形ListBox控件实现的Silverlight奖品轮盘动画。本文介绍了一个使用Silverlight Pathlistbox设计的类似奖品轮盘。

背景

Silverlight Pathlistbox 是一个令人兴奋的控件,它允许您使用ListBox,但将项目定位在自定义路径上。本文介绍了一个用于Pathlistbox 的Silverlight模板控件,以及用于用户界面面板和帮助(作为HTML页面)的自定义控件。资源字典已用于模块化XAML代码。

输出

运行程序时,您会看到如下所示的页面。实时演示可在以下网站找到: PathListBox 奖品轮盘实时演示

Start_Screen

Start_Screen

奖品轮盘可以用文本或图像数据加载。开始按钮会旋转奖品轮盘并随机选择一个项目作为获奖者。轮盘大小可以通过Width和Height的NumericUpDown控件更改(最小300,最大500,增量25,默认值400)。轮盘也可以通过Angle NumericUpDown 控件旋转(最小0,最大360,增量1,默认值90)。项目方向可以通过Orientation CheckListBox更改,角度通过Angle NumericUpDown 控件(最小0,最大360,增量30,默认值270)更改,大小通过Scale NumericUpDownControl (最小1,最大100,增量1,默认值10)更改。

代码描述

主页

MainPage 有2个选项卡:奖品选项卡和帮助选项卡。奖品选项卡包含一个Pathlistbox 模板控件和一个控件面板用户控件。帮助选项卡包含帮助用户控件。主页面还有一个单选按钮,用于选择控件面板位于左侧还是右侧。当更改此单选按钮的选择时,Grid的列定义会被更改,以便将控件面板放置在奖品控件的左侧或右侧。主页面加载后,会设置Initialize_Class1

private void Initialize()
        {
            Initialize_Class1 initialize_class1 = new Initialize_Class1();
            initialize_class1.cp_control1 = this.cp_control1;
            initialize_class1.plb_t_c1 = this.plb_t_c1;
            initialize_class1.Initialize();
        }

查看如何引用主页面的cp_control1 plb_t_c1来设置Initialize_class1。此技术已被用于保持类的模块化,以便它们可以被重用。

初始化类

控件面板组合框使用此类中的文本文件名进行填充。图像和文本按钮的点击事件处理程序也在此处设置。当用户更改下拉组合框中的文本文件、文本或图像按钮时,PathListBox 的路径和数据会重新加载。控件面板项的角度和缩放的绑定也在此处初始化。在描述奖品轮盘控件时,我们将进一步讨论绑定。

private void Set_Binding()
{
plb_t_c1.Item_Angle = 270.0;
plb_t_c1.Item_Scale = 10.0;
cp_control1.scale_numericupdown.SetBinding(NumericUpDown.ValueProperty,
new Binding("Item_Scale") { Source = plb_t_c1, Mode = BindingMode.TwoWay });
cp_control1.Angle_numericupdown.SetBinding(NumericUpDown.ValueProperty,
new Binding("Item_Angle") { Source = plb_t_c1, Mode = BindingMode.TwoWay });
}

奖品轮盘控件

这是一个模板控件,用于生成路径ListBox以及一个用于显示获奖者的堆栈面板。模板控件的XAML文件位于Themes文件夹中的Generic.XAML文件中。此模板控件包含一个名为“PLB_Canvas”的Canvas,一个名为“pathListbox1”的PathListBox控件,一个用于显示获奖者的Stack Panel,以及几个媒体元素,用于在奖品轮盘旋转时产生音频效果。

PathListBox 控件是最有趣的部分,代码如下:

<ec:PathListBox x:Name="pathListbox1" HorizontalAlignment="Left" 
VerticalAlignment="Top" WrapItems="True"
StartItemIndex="0" 
ItemContainerStyle="{StaticResource PLB_ItemStyle1}" 
>
<ec:PathListBox.LayoutPaths >
<ec:LayoutPath FillBehavior= "NoOverlap" Distribution="Even" 
Orientation="OrientToPath" Capacity="50" >
</ec:LayoutPath>
</ec:PathListBox.LayoutPaths>
<i:Interaction.Behaviors >
<Expression_Samples_PathListBoxUtils:PathListBoxScrollBehavior >
<i:Interaction.Triggers>
<i:EventTrigger >
<i:InvokeCommandAction CommandName="DecrementCommand"/>
</i:EventTrigger>
<i:EventTrigger >
<i:InvokeCommandAction CommandName="IncrementCommand" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Expression_Samples_PathListBoxUtils:PathListBoxScrollBehavior>
</i:Interaction.Behaviors>
</ec:PathListBox>

Pathlistbox 使用来自资源字典PathListBoxItemStyle_Dictionary1.xaml的样式PLB_ItemStyle1 。此样式是一个通用的PathListBox,但以下网格变换部分有所不同。

<Grid.RenderTransform >
    <CompositeTransform 
    ScaleY="{Binding Value, ElementName=scale_numericupdown, 
	Converter={StaticResource DivisionConverter}, ConverterParameter=10}" 
    ScaleX="{Binding Value, ElementName=scale_numericupdown, 
	Converter={StaticResource DivisionConverter}, ConverterParameter=10}" 
    Rotation= "{Binding Value, ElementName=Angle_numericupdown}"
    />
</Grid.RenderTransform>

这是基于Christian Schormann在文章 electric beach Blend 4: About Path Layout, Part II 中关于如何在PathListBox中变换项目的想法设计的。这些变换使代码能够旋转或缩放项目。变换还使用Division转换器将Scale NumericUpDown 控件的值除以10

另一个挑战是将这些变换绑定到NumericUpDown 控件。由于这些依赖属性是通过模板控件生成的,因此创建了2个依赖属性,如下面的代码所示:

#region Dependency Properties

public static readonly DependencyProperty Item_AngleProperty =
DependencyProperty.Register("Item_Angle",
typeof(double),
typeof(PLB_T_C1),
new PropertyMetadata(null));
public static readonly DependencyProperty Item_ScaleProperty =
DependencyProperty.Register("Item_Scale",
typeof(double),
typeof(PLB_T_C1),
new PropertyMetadata(null));

#endregion

#region Public Properties

public double Item_Angle
{
get { return (double)GetValue(Item_AngleProperty); }
set { SetValue(Item_AngleProperty, value); }
}
public double Item_Scale
{
get { return (double)GetValue(Item_ScaleProperty); }
set { SetValue(Item_ScaleProperty, value); }
}

#endregion

以下2个属性在OnApplyTemplate()中声明和初始化。这有助于获取对Pathlistbox 控件和Canvas的引用,以便在代码中进行进一步的操作。

public PathListBox plistbox;
public Canvas PLB_Canvas;
----
----
public override void OnApplyTemplate()
{
base.OnApplyTemplate();

PLB_Canvas = this.GetTemplateChild("PLB_Canvas") as Canvas;
foreach (var child in PLB_Canvas.Children)
     {
    if (child is PathListBox)
    {
    plistbox = child as PathListBox;
    }
     }
}

触发器中的DecrementCommand IncrementCommand 用于旋转奖品轮盘。

以下资源用于在奖品轮盘旋转期间播放的音频文件:

<Canvas.Resources>
<MediaElement x:Name="dingdingFile" Source="/audio/dingding.mp3" AutoPlay="False">
</MediaElement>
<MediaElement x:Name="lonloffFile" Source="/audio/lonloff.mp3" AutoPlay="False">
</MediaElement>
</Canvas.Resources> 

路径加载类

PathListBox 的路径被设置为一个椭圆。椭圆的高度、宽度和旋转角度可以调整。使用一个块状箭头指向获奖者。

以下代码会在它们存在时删除椭圆和块状箭头。

public void Load_Path()
{
//Remove ellipse and block arrow if it exists
Remove_shapes_if_exist();
//Add ellipse
Add_Shapes();
el1.RenderTransform = rt;
}
private void Remove_shapes_if_exist()
{
Ellipse ellipse_toremove = null;
BlockArrow blockarrow_toremove = null;
foreach (var item in plb_t_c1.PLB_Canvas.Children)
{
var type_name = item.GetType();
if (type_name == typeof(Ellipse))
{
ellipse_toremove = item as Ellipse;
}

if (type_name == typeof(BlockArrow))
{
blockarrow_toremove = item as BlockArrow;
}
}

if (ellipse_toremove != null)
{
plb_t_c1.PLB_Canvas.Children.Remove(ellipse_toremove);
}

if (blockarrow_toremove != null)
{
plb_t_c1.PLB_Canvas.Children.Remove(blockarrow_toremove);
}
}

以下代码添加了椭圆和块状箭头。高度、宽度和角度数字增量框;淡入淡出和方向复选框的事件也在此处设置。

private void Add_Shapes()
{
//ellipse
el1.Style = Application.Current.Resources["ellipse_style_0"] as Style;
plb_t_c1.PLB_Canvas.Children.Add(el1);
plb_t_c1.plistbox.LayoutPaths[0].SourceElement = el1;
//blockarrow
blockarrow1.Style = Application.Current.Resources["Left_Block_Arrow_Style"] as Style;
plb_t_c1.PLB_Canvas.Children.Add(blockarrow1); 
Locate_Ellipse();
//ellipse width, height changed events
cp_control1.Height_numericupdown.ValueChanged += 
new RoutedPropertyChangedEventHandler<double>(Height_numericupdown_ValueChanged);
cp_control1.Width_numericupdown.ValueChanged += 
new RoutedPropertyChangedEventHandler<double>(Width_numericupdown_ValueChanged); 
// ellipse angle binding
cp_control1.Ellipse_Angle_numericupdown.SetBinding(NumericUpDown.ValueProperty,
new Binding("Angle") { Source = rt, Mode = BindingMode.TwoWay });
rt.Angle = 90;
//Items Orientation
cp_control1.Orientation_CheckBox.Click += 
	new RoutedEventHandler(Orientation_CheckBox_Click);
//Items Fade
if (cp_control1.Fade_CheckBox.IsChecked == true)
{
plb_fade_class.Fade_Selected_Item(plb_t_c1.plistbox);
}
cp_control1.Fade_CheckBox.Click += new RoutedEventHandler(Fade_CheckBox_Click);
}

方向点击事件代码如下:

void Orientation_CheckBox_Click(object sender, RoutedEventArgs e)
{
if (cp_control1.Orientation_CheckBox.IsChecked == true)
{
plb_t_c1.plistbox.LayoutPaths[0].Orientation = 
Microsoft.Expression.Controls.Orientation.OrientToPath;
}
else
{
plb_t_c1.plistbox.LayoutPaths[0].Orientation = 
Microsoft.Expression.Controls.Orientation.None;
}
}

高度和宽度numericupdown 控件的事件代码如下:首先更改椭圆的宽度和高度,然后进行调整以使椭圆和块状箭头位于正确的位置。

private void Locate_Ellipse()
{
el1.Width = cp_control1.Width_numericupdown.Value;
el1.Height = cp_control1.Height_numericupdown.Value;
double leftoffset = 0;
double topoffset = 0;
double width = cp_control1.Width_numericupdown.Value;
double height = cp_control1.Height_numericupdown.Value;
double difference = Math.Abs(width - height);
if (width > height)
{
leftoffset = difference / 2;
topoffset = -difference / 2;
}
else
{
topoffset = difference / 2;
leftoffset = -difference / 2;
}
Canvas.SetTop(el1, leftoffset);
Canvas.SetLeft(el1, topoffset);
Locate_BlockArrow(leftoffset, topoffset);
}

private void Locate_BlockArrow(double leftoffset, double topoffset)
{
double blockarrow1_y = el1.Margin.Top + 
cp_control1.Height_numericupdown.Value / 2 + leftoffset - blockarrow1.Height / 2;
double blockarrow1_x = cp_control1.Width_numericupdown.Value + 
el1.Margin.Left + ((App)Application.Current).plb_item_width + topoffset * 2;
Canvas.SetTop(blockarrow1, blockarrow1_y );
Canvas.SetLeft(blockarrow1, blockarrow1_x);
}

淡入淡出是一个有趣的主题,将在下面的单独部分中介绍。

数据加载类

此类包含2个可观察集合,一个用于文本,一个用于图像。

ObservableCollection<TextBox> name_textbox_list = new ObservableCollection<TextBox>();
ObservableCollection<Image> image_list = new ObservableCollection<Image>();

如果用户选择“文本”按钮,则从组合框选择的文本文件中加载文本。

 public void Load_Data()
{
if (cp_control1.Text_Button.IsChecked == true) Populate_Text_ListPanel();
if (cp_control1.Images_Button.IsChecked == true) Populate_Pictures_ListPanel();
if (cp_control1.Orientation_CheckBox.IsChecked == true)
{
plb_t_c1.plistbox.LayoutPaths[0].Orientation = 
	Microsoft.Expression.Controls.Orientation.OrientToPath;
}
else
{
plb_t_c1.plistbox.LayoutPaths[0].Orientation = 
	Microsoft.Expression.Controls.Orientation.None;
}
}
private void Populate_Text_ListPanel()
{
pathlistbox1.ItemsSource = null;
name_textbox_list.Clear();
GetData_Combo();
int ipickcolor = 0;
int iwidth = 0;
int iwidth_max = 15; //restrict to 15 characters
for (int itextcount = itextmincount; itextcount < name_list.Count; itextcount++)
{
TextBox text1 = new TextBox();
text1.FontSize = 16;
text1.Height = text1.FontSize * 2;
text1.TextAlignment = TextAlignment.Center;
text1.Foreground = new SolidColorBrush(Colors.Black); 
text1.Text = name_list[itextcount].name1;
if (text1.Text.Length > iwidth)
{
iwidth = text1.Text.Length;

}
System.Windows.Media.Color[] myColorArray = 
	{ Colors.Blue, Colors.Red, Colors.Green, Colors.Purple };
text1.Background = new SolidColorBrush(colorpicker.ColorName_Array[ipickcolor]);
ipickcolor++;
if (ipickcolor == (colorpicker.ColorName_Array.Length)) ipickcolor = 0;
name_textbox_list.Add(text1);
}
if (iwidth > iwidth_max)
{
iwidth = iwidth_max;
}
foreach (var item in name_textbox_list)
{
item.Width = item.FontSize + iwidth * item.FontSize / 2;
}
((App)Application.Current).plb_item_width =Convert.ToInt16(name_textbox_list[0].Width)/2;

itemheight = name_textbox_list[0].Height + 10;
pathlistbox1.ItemsSource = name_textbox_list;
// int start_item_index = (name_list.Count - (name_textbox_list.Count - 1) / 4);
int start_item_index = 0;
pathlistbox1.StartItemIndex = start_item_index;
pathlistbox1.SelectedIndex = 0;
}
 private void GetData_Combo()
{
try
{
string selected_file = "text_files/" + 
			cp_control1.Text_DropDownList.SelectedItem.ToString()+".txt";
name_list = Load_Names(selected_file);
}
catch (Exception ex)
{
string errorex = ex.Message.ToString();
}
}
public ObservableCollection<name> Load_Names(string fileinfo)
{
ObservableCollection<name> temp_list = new ObservableCollection<name>();
StreamResourceInfo f1 = Application.GetResourceStream(
new Uri(fileinfo,
UriKind.Relative));

StreamReader r = new StreamReader(f1.Stream);

using (r)
{
string line;
while ((line = r.ReadLine()) != null)
{
name name1 = new name();
name1.name1 = line;
temp_list.Add(name1);
}
}
r.Close();
return temp_list;
}

如果选择Imagebutton ,则从images文件夹加载images img0.jpg --- img15.jpg

 private void Populate_Pictures_ListPanel()
{
pathlistbox1.ItemsSource = null;

image_list.Clear();
for (int iimagecount = iimagemincount; iimagecount < iimagemaxcount; iimagecount++)
{
Image image1 = new Image();
image1.Width = 70;
image1.Height = 70;
image1.Source = GetImage("/images/img" + iimagecount.ToString() + ".jpg");
image1.Stretch = Stretch.Fill;
image_list.Add(image1);
}

pathlistbox1.ItemsSource = image_list;
itemwidth = image_list[0].Width + 10;
itemheight = image_list[0].Height + 10;
int start_item_index = 0;
pathlistbox1.StartItemIndex = start_item_index;
pathlistbox1.SelectedIndex = 0;
((App)Application.Current).plb_item_width = Convert.ToInt16(image_list[0].Width) / 2;
}

private ImageSource GetImage(string path)
{
return new BitmapImage(new Uri(path, UriKind.RelativeOrAbsolute));
}

奖品类

以下代码片段展示了Prize 类的主要方面。Spin Timer用于控制奖品轮盘的旋转,Tone Timer用于控制声音。以下代码将Spin Timer初始化为200毫秒的滴答,启动它,然后选择一个随机数。步数设置为随机数+2次旋转的计数,并启动轮盘旋转音调。PathListBox 行为集合的持续时间设置为200毫秒,步长设置为1

 private void prize_start()
{
Prize_Winner_TextBlock.Text = "";
Prize_Winner_Image.Source = null;
plb_fade_class.unfade_all_items(plb_t_c1.plistbox);
plb_t_c1.plistbox.SelectedIndex = current_index;
Initialize_SpinTimer();
Spin_Timer.Start();
Random random_prize = new Random();
int randomstartnumber = plb_t_c1.plistbox.Items.Count * 2;
int random_prize_number = random_prize.Next(0, plb_t_c1.plistbox.Items.Count);
prize_next_number = randomstartnumber - random_prize_number;
pbsb.Amount = 1;
pbsb.Duration = TimeSpan.FromMilliseconds(200);

tone1.Play();
}
private void Initialize_SpinTimer()
{
Spin_Timer.Stop();
Spin_Timer.Interval = TimeSpan.FromMilliseconds(200);
tone1.Stop();
tone2.Stop();
}
 private void Prize_pathListBox_behavior_initialize()
{
behavior_collection = System.Windows.Interactivity.Interaction.GetBehaviors
			(plb_t_c1.plistbox);
pbsb = (Expression.Samples.PathListBoxUtils.PathListBoxScrollBehavior)
			behavior_collection[0];
} 

在每次Spin Timer滴答时,以下代码会将PrizeWheel 前进1步,并将prize_next_number 减去1。当prize_next_number 达到10时,计时器和Pathlistbox 的持续时间会更改为400毫秒,这会使PrizeWheel 减速。当prize_next_number 0时,PrizeWheel 停止,并启动第二个音调和Tone Timer。奖品获奖者堆栈面板会根据情况填充获胜者姓名或图像。

 void Spin_Timer_Tick(object sender, EventArgs e)
{
if (prize_next_number == 10)
{
Spin_Timer.Interval = TimeSpan.FromMilliseconds(400);
pbsb.Duration = TimeSpan.FromMilliseconds(400);
}

if (prize_next_number > 0)
{
pbsb.DecrementCommand.Execute(null);
prize_next_number--;
current_index--;
if (current_index < 0) current_index = plb_t_c1.plistbox.Items.Count - 1;
plb_t_c1.plistbox.SelectedIndex = current_index;
if (cp_control1.Fade_CheckBox.IsChecked == true)
{
plb_fade_class.Fade_Selected_Item(plb_t_c1.plistbox);
}
}
else
{
Spin_Timer.Stop();
tone1.Stop();
tone2.Play();
Tone_Timer.Interval = TimeSpan.FromMilliseconds(1000);
Tone_Timer.Start();
if (cp_control1.Fade_CheckBox.IsChecked == true)
{
plb_fade_class.Fade_Selected_Item(plb_t_c1.plistbox);
}

if (cp_control1.Text_Button.IsChecked == true)
{
Prize_Winner_TextBlock.Text = (plb_t_c1.plistbox.SelectedItem as TextBox).Text;
}
if (cp_control1.Images_Button.IsChecked == true)
{
Prize_Winner_Image.Source = (plb_t_c1.plistbox.SelectedItem as Image).Source;
}
}
}

淡入淡出代码

当选择淡入淡出时,块状箭头指向的项目将被高亮显示,其余项目将被淡出,如下所示:

Start_Screen

此设计基于James在Coffeefueled.org 上发表的一篇优秀博客 Silverlight PathListBox Fading Unselected Items CoffeeFueled。正如James所指出的,您需要使用以下代码来查找PathListBoxItem 网格:

 public List<Grid> GetChildGrid(DependencyObject parent)
{
List<Grid> children = new List<Grid>();
int count1 = VisualTreeHelper.GetChildrenCount(
VisualTreeHelper.GetChild(
parent, 0));
int count = VisualTreeHelper.GetChildrenCount(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
parent, 0), 0), 0), 0));
var test1 = VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(

parent, 0), 0), 0), 0);

for (int i = 0; i < count; i++)
{
children.Add((Grid)VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild(
VisualTreeHelper.GetChild( //PathListBoxItem
VisualTreeHelper.GetChild(
parent, 0), 0), 0), 0), i), 0));
}

return children;
}

以下代码中有2个动画。第一个动画淡出除选定项外的所有项,第二个动画则移除所有项的淡出效果。

 public void FadeAnimation(Grid target, double timespan, double opacity)
{
DoubleAnimationUsingKeyFrames itemFade = new DoubleAnimationUsingKeyFrames();
itemFade.Duration = TimeSpan.FromSeconds(timespan);

Storyboard.SetTargetProperty(itemFade, new PropertyPath("(ec:Grid.Opacity)"));
Storyboard.SetTarget(itemFade, target);

EasingDoubleKeyFrame fadeFrame = new EasingDoubleKeyFrame();
fadeFrame.Value = opacity;
fadeFrame.KeyTime = TimeSpan.FromSeconds(timespan);

itemFade.KeyFrames.Add(fadeFrame);

Storyboard fade = new Storyboard();
fade.Children.Add(itemFade);

fade.Begin();
}
public void unfade_all_items(Microsoft.Expression.Controls.PathListBox Plb1_fade)
{
List<Grid> children = GetChildGrid(Plb1_fade);
if (children != null)
{
for (int i = 0; i < children.Count; i++)
{

FadeAnimation(children[i], 0.5, 1);
}
}
}

帮助控件

这是基于David Anson的文章 将HTML引入Silverlight [HtmlTextBlock使富文本显示变得容易!] - Delay's Blog - Site Home - MSDN BlogsHTMLTextBlock 用于将一些HTML引入Silverlight。此技术有助于在此自定义TextBlock中加载常规HTML页面。

在XAML中创建一个HTMLTextBlock

<local:HtmlTextBlock x:Name="htmlTextBlock" 
Canvas.Left="2" 
Canvas.Top="2" 
Width="700" Height="700" 
TextWrapping="Wrap" 
UseDomAsParser="true" />

使用代码隐藏中的以下代码加载HTMLPage1.htm

 void Help_Control1_Loaded(object sender, RoutedEventArgs e)
{
if (DesignerProperties.GetIsInDesignMode(this))

return;
string selected_file = "Help_Control/HTMLPage1.htm";
htmlTextBlock.Text = Load_HTML_file(selected_file);
}
public string Load_HTML_file(string fileinfo)
{
string htmlstring = "";
StreamResourceInfo f1 = Application.GetResourceStream(
new Uri(fileinfo,
UriKind.Relative));

StreamReader r = new StreamReader(f1.Stream);

using (r)
{
string line;
while ((line = r.ReadLine()) != null)
{
htmlstring += line;
}
}
r.Close();
return htmlstring;
}

UI

UI使用了参考11到14中描述的几项技术。

关注点

这是一个有趣的演示。我认为我已经实现了代码结构,这将有助于我添加更多功能。这是我计划撰写的关于我的PathListBox冒险之旅系列的第一部分。在接下来的文章中,我将介绍如何为PathListBox使用不同的路径,创建自定义路径以及更多路径动画。敬请关注!

参考文献

  1. 使用自定义圆形ListBox控件实现的Silverlight奖品轮盘动画 - CodeProject
  2. PathListBox 奖品轮盘实时演示
  3. Electric beach » Blend 4: 关于路径布局,第二部分
  4. 将HTML引入Silverlight [HtmlTextBlock使富文本显示变得容易!] - Delay's Blog - Site Home - MSDN Blogs
  5. Silverlight PathListBox 淡出未选中的项目 « CoffeeFueled.
  6. 如何在Silverlight自定义控件中实现模板绑定?
  7. 热门婴儿名字
  8. Silverlight 4 PathListBox控件入门指南(第一部分) - CodeProject
  9. PathListBox简介 | .tutorials.pathlistbox | Microsoft Design .toolbox
  10. Silverlight模板控件 « Johan's Blog
  11. Silverlight工具包中的NumericUpDown控件 | Ning Zhang's Blog
  12. Silverlight中的玻璃球按钮 - CodeProject
  13. Silverlight中的颜色:我需要更大的蜡笔盒!: Microsoft 官方Silverlight.NET论坛.
  14. 分组切换按钮.
  15. Silverlight 4 PathListBox 控件入门指南 (第一部分).
  16. HTML 目录生成器.

历史

这是本文的第一个版本。

© . All rights reserved.