相信大家接触过不少win窗体控件ChildWin子窗口就的sl自带的一个
而且网上也有很多类似的控件,而今天我和大家分享下自己制作个win窗体控件,希望对初学sl的朋友在学习自定义控件时有帮助。
首先先明确下两个概念用户控件和模板化控件。
用户控件是继承UserControl而来的控件,由于UserControl不支持模板,所以它只能用于组合现有控件件而不能用于设计可定制外观的控件。
模板化控件是继承自ContentControl, Control等支持模板的而来的控件,ContentControl是继承自Control
他只能有一个可视化根元素,这一点和UserControl一样。而我们这次就是要用ContentControl实现我们的Win窗体控件。
下面大家先看下ContentControl的模板
1.
<Style TargetType="ContentControl">
2.
<Setter Property="Foreground" Value="#FF000000"/>
3.
<Setter Property="HorizontalContentAlignment" Value="Left"/>
4.
<Setter Property="VerticalContentAlignment" Value="Top"/>
5.
<Setter Property="Template">
6.
<Setter.Value>
7.
<ControlTemplate TargetType="ContentControl">
8.
<ContentPresenter
9.
Content="{TemplateBinding Content}"
10. ContentTemplate="{TemplateBinding ContentTemplate}"
11. Cursor="{TemplateBinding Cursor}"
12. Margin="{TemplateBinding ;Padding}"
13. HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
14. VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
15. </ControlTemplate>
16. </Setter.Value>
17. </Setter>
18. </Style>
19.
20.
在ContentControl的模板中只有一个ContentPresenter来定制ContentControl的内容区域
用TemplateBinding将模板属性绑定到控件公开的属性属性这里就不做介绍了有兴趣的朋友可以去看下sl的文档。
接下来开始我们的控件制作之旅,在VS或Blend新建个项目
由于我这是使用VS在模板里编辑外观和麻烦所以先在MainPage里设计好外观再加到模板中
首先拖个矩形到界面调整属性使它填充界面(去掉长宽和Margin等)加上圆角
1.
<Rectangle x:Name="BackRect" Fill="#932323B7" RadiusX="5" RadiusY="5" Grid.RowSpan="4" Grid.ColumnSpan="3"/>
接下来再拖个矩形到界面调整下使它
1.
<Rectangle Name="Win_Content" Grid.Row="2" Grid.Column="1" Fill="white"/>
对接着对主界面Grid分行分列
1.
<Grid.ColumnDefinitions>
2.
<ColumnDefinition Width="5*" />
3.
<ColumnDefinition Width="390*" />
4.
<ColumnDefinition Width="5*" />
5.
</Grid.ColumnDefinitions>
6.
<Grid.RowDefinitions>
7.
<RowDefinition Height="5*" />
8.
<RowDefinition Height="15*" />
9.
<RowDefinition Height="275*" />
10. <RowDefinition Height="5*" />
11. </Grid.RowDefinitions>
接下来拖曳五个矩形到界面
1.
<Rectangle Grid.Column="1" Name="Win_Move" Fill="#932323B7" Grid.RowSpan="2"/>
2.
3.
<Rectangle Grid.Row="2" Grid.Column="2" x:Name="Side_Right" Cursor="SizeWE"></Rectangle>
4.
<Rectangle Grid.Row="2" x:Name="Side_Left" Cursor="SizeWE"></Rectangle>
5.
<Rectangle Grid.Row="3" Grid.Column="1" x:Name="Side_Botton" Cursor="SizeNS"></Rectangle>
6.
<Rectangle Grid.Column="1" x:Name="Side_Top" Cursor="SizeNS"></Rectangle>
大家可以看到后面是个矩形我设置了Cursor并把它们放在是个边缘上,这个是为后面实现窗体拉伸用的
接着拖曳个TextBlock
1.
<TextBlock Grid.Column="1" Grid.Row="1" Name="Win_Title" HorizontalAlignment="Left" Grid.RowSpan="1"/>
这是为了实现win窗体的标题
接着拖个Grid并
设置属性
在里面添加三个按钮
1.
<Grid Grid.Column="1" Grid.RowSpan="2" HorizontalAlignment="Right" Name="ButtonGroup" Width="75">
2.
<Grid.ColumnDefinitions>
3.
<ColumnDefinition Width="20*" />
4.
<ColumnDefinition Width="20*" />
5.
<ColumnDefinition Width="20*" />
6.
</Grid.ColumnDefinitions>
7.
<Button Style="{StaticResource btnMinimizeStyle}"></Button>
8.
<Button Grid.Column="1" Style="{StaticResource btnWindowedStyle}"></Button>
9.
<Button Grid.Column="2" Style="{StaticResource btnCloseStyle}"></Button>
10. </Grid>
本人没啥艺术天赋就到网上随便找了三个按钮样式!!!!!!!!!!!!!!!
接着实现win窗体的阴影效果
这个也很简单只要使用Effect就能实现
先复制一下背景矩形的xaml
然后加Effect
1.
<Rectangle Fill="Black" RadiusX="5" RadiusY="5" Grid.RowSpan="4" Grid.ColumnSpan="3">
2.
<Rectangle.Effect>
3.
<BlurEffect Radius="9"></BlurEffect>
4.
</Rectangle.Effect>
5.
</Rectangle>
6.
<Rectangle x:Name="BackRect" Fill="#932323B7" RadiusX="5" RadiusY="5" Grid.RowSpan="4" Grid.ColumnSpan="3">
7.
<!--<Rectangle.Effect>
8.
<DropShadowEffect></DropShadowEffect>
9.
</Rectangle.Effect>-->
10. </Rectangle>
现在窗体的基本外观已经完成
在界面里加个ContentPresenter
1.
<ContentPresenter Grid.Row="2" Grid.Column="1"></ContentPresenter>
现在项目里添加个模板控件把cs文件里的基类改为ContentControl
并设置ContentPresenter属性
1.
<ContentPresenter Grid.Row="2" Grid.Column="1" Content="{TemplateBinding Content}"
2.
HorizontalAlignment="{TemplateBinding HorizontalAlignment}"
3.
VerticalAlignment="{TemplateBinding VerticalAlignment}"
4.
ContentTemplate="{TemplateBinding ContentTemplate}"
5.
Cursor="{TemplateBinding Cursor}"
6.
Margin="{TemplateBinding ;Padding}"></ContentPresenter>
接着你可以在编译项目拖曳个Win窗体控件或新建个Win窗体控件为基类的控件
1.
<my:WInControl x:Class="anyePath.SilverlightControl1"
2.
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4.
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5.
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6.
mc:Ignorable="d"
7.
d:DesignHeight="300" d:DesignWidth="400" xmlns:my="clr-namespace:anyePath" xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk">
8.
9.
<Grid x:Name="LayoutRoot">
10. <Button Content="Button" Height="64" HorizontalAlignment="Left" Margin="68,48,0,0" Name="button1" VerticalAlignment="Top" Width="119" />
11. <RadioButton Content="RadioButton" Height="44" HorizontalAlignment="Left" Margin="53,152,0,0" Name="radioButton1" VerticalAlignment="Top" Width="88" />
12. <ComboBox Height="47" HorizontalAlignment="Left" Margin="218,18,0,0" Name="comboBox1" VerticalAlignment="Top" Width="135" />
13. </Grid>
14. </my:WInControl>
这里先讲到这我们已经完成了控件的基本外观
在下篇文章里我们就要为窗体添加功能
如移动拉伸标题依赖属性最大化最小化等等 的设置
这章就到这里希望对大家有帮助!!!!!!
在上一篇文章里我们完成了Win窗体的基本外观,但仅仅只有个外观我想大家都不会满意,那在我们
就开始给窗体添加功能。
在这为了简化步骤,就以父容器为Canvas考虑,由于他是绝对布局的所以实现移动较方便,大家如果要是控件的使用范围些
可以去试试用平移变换实现窗体移动。
首先 还是先明确下一些基础点,由于
窗体的外观是在模板里定义的,所以于窗体有关的部件我们不能直接访问,不过访问的方法有好几种,我在这介绍两种。
1.我们可以在那些模板里的部件加上Loaded事件,再在这个事件的处理函数里把,这个部件的引用保存下来。
2.是重写OnApplyTemplate方法在这个方法里调用GetTemplateChild获得部件引用。
我这是利用第二种方法,好现在开始实现功能
- public override void OnApplyTemplate()
- {
- base.OnApplyTemplate();
- TitleC = GetTemplateChild("Win_Title") as TextBlock;//是呈现窗体的TextBlock
- BackImage = GetTemplateChild("BackRect") as Rectangle;//是背景矩形
- TitleC.Text = Title;//Title是窗体标题的依赖属性
- //窗体顶部矩形
用于移动矩形
- (GetTemplateChild("Win_Move") as Rectangle).MouseLeftButtonDown += new MouseButtonEventHandler(WInControl_MouseLeftButtonDown);
- //窗体四边的矩形,还记得吗上篇文章
我给他设置Cursor属性这就是为了实现 鼠标移动到窗体边缘的变化 主要要把那四个矩形的背景不能透明,即使0.05也行 就不能透明
- (GetTemplateChild("Side_Right") as Rectangle).MouseLeftButtonDown+=new MouseButtonEventHandler(WInControl_MouseLeftButtonDown);
- (GetTemplateChild("Side_Left") as Rectangle).MouseLeftButtonDown += new MouseButtonEventHandler(WInControl_MouseLeftButtonDown);
- (GetTemplateChild("Side_Top") as Rectangle).MouseLeftButtonDown += new MouseButtonEventHandler(WInControl_MouseLeftButtonDown);
- (GetTemplateChild("Side_Botton") as Rectangle).MouseLeftButtonDown += new MouseButtonEventHandler(WInControl_MouseLeftButtonDown);
- //就是窗体上的那三个按钮
- (GetTemplateChild("MinB") as Button).Click +=new RoutedEventHandler(WInControl_Click);
- (GetTemplateChild("MaxB") as Button).Click +=new RoutedEventHandler(WInControl_Click);
- (GetTemplateChild("CloseB") as Button).Click += new RoutedEventHandler(WInControl_Click);
- }
-
这里就是移动窗体和尺寸拉伸的 代码
protected void WInControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
- {
- Rectangle rect = sender as Rectangle;
-
- rect.MouseMove += new MouseEventHandler(rect_MouseMove);
- rect.MouseLeftButtonUp += new MouseButtonEventHandler(rect_MouseLeftButtonUp);
- rect.CaptureMouse();
- LastPoint = e.GetPosition(Owner);
- // 这里就注册鼠标移动事件
和 弹起事件
- }
-
- void rect_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
- {
-
- Rectangle rect = sender as Rectangle;
- rect.MouseMove -= rect_MouseMove;
- rect.MouseLeftButtonUp -= rect_MouseLeftButtonUp;
- rect.ReleaseMouseCapture();
- //这里也没什么好说
- }
-
- protected void rect_MouseMove(object sender, MouseEventArgs e)
- {
- Rectangle rect = sender as Rectangle;
- Point p = e.GetPosition(Owner);
- //这里按照Name判断执行什么操作
- switch (rect.Name)
- {
- case "Win_Move": MoveWin(p); break;
- case "Side_Right": ChangeRightWin(p); break;
- case "Side_Left": ChangeLeftWin(p); break;
- case "Side_Top": ChangeTopWin(p); break;
- case "Side_Botton": ChangeBottomWIn(p); break;
- }
-
- }
- //而这里是移动的主要逻辑 如果大家做过拖曳控件的功能就非常简单
- public void MoveWin(Point e)
- {
//LastPoint在鼠标点击事件里赋值 记录了上一个事件里鼠标的位置
- // 而这用这事鼠标的位置减去上一时刻的鼠标位置 然后再移动窗体这个差值
- //这两个判断语句是防止窗体整个移界面
- Point D_point = new Point(e.X - LastPoint.X, e.Y - LastPoint.Y);
- if (!(Canvas.GetTop(this) <= 0 && D_point.Y <= 0))
- {
- if (!(Canvas.GetTop(this) > Owner.ActualHeight && D_point.Y >= 0))
- {
- Canvas.SetLeft(this, Canvas.GetLeft(this) + D_point.X);
- Canvas.SetTop(this, Canvas.GetTop(this) + D_point.Y);
- //再保存位置
- LastPoint = e;
- }
- }
-
- }
- //下面四个方法是改变窗体大小的
- void ChangeBottomWIn(Point e)
- { //这个是底边拉伸窗体
也很好理解 就是计算出 当鼠标点下 后每次Move的差值 在把这个差值加到窗体高度上
- double M_mun = e.Y - Canvas.GetTop(this) - this.Height;
- if (this.Height >= 100 || M_mun > 0)
- this.Height += M_mun;
- }
-
- void ChangeTopWin(Point e)
- {
//这是顶部拉伸的代码
但这不能简单把移动差值加到 控件高度上
- //这样会造成向上拉伸 向下变长的现象 我们必须先向上移动控件 这个差值再加
- double M_mun = Canvas.GetTop(this) - e.Y;
- if(this.Height >= 100 || M_mun >0)
- this.Height += M_mun;
- LastPoint = e;
- Canvas.SetTop(this, LastPoint.Y);
- }
- //接下来的左边和右边就不讲 原理和上面一样
- void ChangeLeftWin(Point e)
- {
- double M_mun = Canvas.GetLeft(this) - e.X;
- if (this.Width >= 100 || M_mun > 0)
- this.Width += M_mun;
- LastPoint = e;
- Canvas.SetLeft(this, LastPoint.X);
- }
-
- void ChangeRightWin(Point e)
- {
- double M_mun = e.X - Canvas.GetLeft(this) - this.Width;
- if (this.Width >= 100 || M_mun > 0)
- this.Width += M_mun;
- }
接下来是实现最大化的功能
接着看代码
- public static readonly DependencyProperty IsMaxmunProperty = DependencyProperty.Register("IsMaxmun", typeof(bool), typeof(WInControl), new PropertyMetadata(false, onIsMaxmunChange)); //这是定义的最大化的依赖属性说是最大化
其实是最大化和窗口化
- public bool IsMaxmun//最大化
- {
- get { return (bool)this.GetValue(IsMaxmunProperty); }
- set { this.SetValue(IsMaxmunProperty, value); }
- }
- public static void onIsMaxmunChange(DependencyObject sender, DependencyPropertyChangedEventArgs e)
- {
- WInControl This = sender as WInControl;
- if (This.IsMaxmun == true)
- This.ToMaxmun();
- else
- This.ToMinnormal();
- }
- protected virtual void ToMaxmun()
- { //把窗体高和宽保存起来
再把窗体设置成与父容器 这里有个Owner是用来设置父容器引用
- //大家也可 用Parent属性 他就是在可视树上的父节点
- LastPoint.X = Canvas.GetLeft(this);
- LastPoint.Y = Canvas.GetTop(this);
- LastWinSize.Width = this.Width;
- LastWinSize.Height = this.Height;
-
- Canvas.SetLeft(this, 0);
- Canvas.SetTop(this, 0);
- this.Height = (Owner).RenderSize.Height;
- this.Width = (Owner).RenderSize.Width;
-
- }
-
- protected virtual void ToMinnormal()
- {
//这里就是还原窗体
- Canvas.SetLeft(this, LastPoint.X);
- Canvas.SetTop(this, LastPoint.Y);
- this.Height = LastWinSize.Height;
- this.Width = LastWinSize.Width;
- }
这就是最大化和窗口化功能 还有一点在最大化时大家要 移除那几个矩形的鼠标点击事件 再在
窗口化添加这个事件 最小化就留给大家自己考虑。
- public static readonly DependencyProperty TitleProperty = DependencyProperty.Register("Title", typeof(string), typeof(WInControl), new PropertyMetadata(string.Empty, OnTitleChanged));
这个是窗体标题
- public string Title
- {
- get { return (string)GetValue(TitleProperty); }
- set { SetValue(TitleProperty, value); }
- }
- public static void OnTitleChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
- {
- WInControl This = sender as WInControl;
- if (This.TitleC != null)
- This.TitleC.Text = This.Title;
- }
这里有一点要注意 由于这个方法比较早调用 而 TitleC没有设置引用 所以要判断下是否为null
这里一部分基本功能已经完成了
不过有两个重要的功能就是关闭 和打开窗体
这里的repeat和repeat2是防止动画被多次设定
动画已经在资源里
只是没有设定作用对象
- public override void Close()
- {
- Storyboard app = Resources["_disappear"] as Storyboard;
- if (repeat == false)
- {
- foreach (var item in app.Children)
- {
- Storyboard.SetTarget(item, this);
- }
- repeat = true;
- }
- app.Begin();
- }
-
- public override void Open()
- {
- Storyboard app = Resources["_appear"] as Storyboard;
- this.Visibility = Visibility.Visible;
- if (repeat2 == false)
- {
- foreach (var item in app.Children)
- {
- Storyboard.SetTarget(item, this);
- }
- repeat2 = true;
- }
- app.Begin();
- }
就这样就可以实现关闭和打开的功能
而且动画由你自己设定
这里先讲到这里 可能有很多不足 但免免强强一个自己写的win窗体控件算完成了
对于那些sl老手可能没什么
但我希望对sl初学的朋友有帮助
还请大家多提意见相互学习 Demo我整理下晚点发上来
希望大家这个基础上继续加强!!!!!!!!!!
演示地址http://anye6488-001-site1.site4future.com/anyePath.web/anyePathTestPage1.html
注:本文转自http://dingtao-wgs.blog.163.com/blog/static/502607142010101754018734/,仅供学习之用。