
2008年9月8日
摘要: 在贴出来上一篇文章后,感觉那个ColorPicker太简单了,于是决定搞个Blend中的那种ColorPicker。由于工作量比较大,所以打算分成几次来完成。 首先说明一下,这个Demo还是属于未完成的阶段,比如ColorPicker的属性只是简单地设置了一个SelectedColor属性,而实际上分为A,R,G,B四个属性比较合适,这样可以在右边直接修改值;又比如,样式实在...
阅读全文
posted @
2008-09-08 16:37 智者千虑 阅读(674) |
评论 (5) |
编辑

2008年9月3日
在斯克迪亚看到一篇WPF动态改变主题颜色的文章,来了兴趣,于是自己搞了个简单的ColorPicker控件。
控件其实很简单,定义了5个依赖属性
FinalBrushProperty, AProperty, RProperty, GProperty, BProperty
然后当A,R,G,B发生变化时,构造新的FinalBrush
在FinalBrush发生变化时,更新ARGB的值。

Code
private static void OnFinalBrushChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ColorPicker picker = sender as ColorPicker;
Color newColor = ((SolidColorBrush)e.NewValue).Color;
picker.A = newColor.A;
picker.R = newColor.R;
picker.G = newColor.G;
picker.B = newColor.B;
picker.RaiseEvent(new FinalBrushChangedEventArgs(FinalBrushChangedEvent, picker, (SolidColorBrush)e.OldValue, (SolidColorBrush)e.NewValue));
}
private static void OnColorChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
ColorPicker picker = sender as ColorPicker;
picker.FinalBrush = new SolidColorBrush(Color.FromArgb(picker.A, picker.R, picker.G, picker.B));
}
在ColorPicker的模板里面,分别放置4个Slider绑定到ARGB,一个Border绑定到FinalBrush以预览结果。

Code
<Style TargetType="{x:Type local:ColorPicker}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ColorPicker}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<DockPanel>
<Border CornerRadius="5" BorderBrush="Black" BorderThickness="1"
Width="60" Height="60" Margin="10"
Background="{TemplateBinding FinalBrush}"/>
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Slider}">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="Minimum" Value="0"/>
<Setter Property="Maximum" Value="255"/>
<Setter Property="SmallChange" Value="1"/>
<Setter Property="LargeChange" Value="15"/>
<Setter Property="Grid.Column" Value="1"/>
</Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="HorizontalAlignment" Value="Right"/>
<Setter Property="Grid.Column" Value="0"/>
</Style>
</Grid.Resources>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="65"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Alpha"/>
<TextBlock Grid.Row="1" Text="Red"/>
<TextBlock Grid.Row="2" Text="Green"/>
<TextBlock Grid.Row="3" Text="Blue"/>
<Slider Grid.Row="0" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=A, Mode=TwoWay}"/>
<Slider Grid.Row="1" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=R, Mode=TwoWay}"/>
<Slider Grid.Row="2" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=G, Mode=TwoWay}"/>
<Slider Grid.Row="3" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=B, Mode=TwoWay}"/>
</Grid>
</DockPanel>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
这个时候,ColorPicker就可以使用了。在Demo里面,我做了一个ColorPickerDialog。它继承自Window,里面放了一个ColorPicker控件,然后将它的FinalBrush属性绑定到资源中的WindowBackground上。这样打开Dialog的时候,它的颜色自动和资源的颜色同步。同时,监听它的ColorPicker的FinalBrushChanged事件,更新资源中的颜色。这样,当我们调节ColorPickerDialog的颜色的时候,Window的颜色同步变化。
当然,你可以做的更完善,比如添加一个确定和取消按钮,当取消的时候恢复原来的颜色。或者放一个CheckBox,指定是否同步预览。
我的美工功底比斯克迪亚差远了,呵呵,就是原始的样式。
源代码下载:http://files.cnblogs.com/RMay/ChangeColor.rar
晕啊,用Chrome编辑有问题。
posted @
2008-09-03 17:14 智者千虑 阅读(1135) |
评论 (4) |
编辑

2008年8月29日
摘要: 【重要】代码有误,我已经更新了可能有时候会有这样的需求,我们的应用程序需要弹出一个窗口,或者是包含多个窗口。同时呢,又不想真正的用Window,尤其是当我们写XBAP应用的时候。恰巧WPF里面又没有MDI…… 当然,我们有几种解决办法。 一种比较简单的办法是,用UserControl仿造一个窗口放在应用程序里面,然后将Visibility设置为隐藏。接着,在我们需要的时候,...
阅读全文
posted @
2008-08-29 12:39 智者千虑 阅读(1378) |
评论 (10) |
编辑

2008年8月22日
摘要: 最近做控件上了瘾,现在把做的一个类似于QQ面板的控件放上来。【分析】从整体来看,这个控件应该同ListBox,ListView这类控件一样,是一个ItemsControl,而中间的项,就是它的Item。因此,为了完成一个这样的控件,至少需要两个东西:GroupShelf:也就是充当容器角色的控件GroupShelfItem:即这个控件中的项其中,GroupShelf需要保证某项的展开同时,其他项被...
阅读全文
posted @
2008-08-22 11:50 智者千虑 阅读(2558) |
评论 (17) |
编辑

2008年8月14日
摘要: 英文原文地址:Simplifying the WPF TreeView by Using the ViewModel Pattern作者:Josh Smith文中代码的下载地址:http://www.codeproject.com/KB/WPF/TreeViewWithViewModel/TreeViewWithViewModelDemo.zip好像需要登录才能下载,我放一个上来吧http://f...
阅读全文
posted @
2008-08-14 16:43 智者千虑 阅读(182) |
评论 (3) |
编辑

2008年7月28日
修改:经过研究,发现只要在Frame上设置JournalEntry.KeepAlive="True"就可以使用第一种绑定到Source的最简单的办法来实现文中的效果。不用自己管理Page
这几天给给别人做了几个Demo,但觉得每个Demo都做一个工程太麻烦,不好管理,于是决定把每个Demo都各自用Page,然后通过一个列表,可以选择各个页面来查看。就如下图的效果:

当从左边选中一项后,右边的Frame会展示相应的内容。
我采用了一个xml文件来描述相关的信息,示例如下:
<?xml version="1.0" encoding="utf-8" ?>
<Pages>
<Page Name="Page1" Uri="/FrameContent;Component/Page1.xaml"/>
<Page Name="Page2" Uri="/FrameContent;Component/Page2.xaml"/>
</Pages>
这是一个非常简单的描述,每个Page项描述这个Page的Name和对应的xaml文件,然后我们可以简单用下面的方式来绑定这些信息来达到目的:
<ListBox x:Name="ListBox1"
ItemsSource="{Binding Source={StaticResource ListSource}}"
DisplayMemberPath="@Name"/>
<!-- 最简单的方式就是用Frame的Source来做绑定,不过这样会导致每次都生成新的实例 -->
<Frame DataContext="{Binding ElementName=ListBox1, Path=SelectedItem}"
Source="{Binding XPath=@Uri}"/>
不过,这样有一个小小的问题在于,每次我切换Page的时候,都将重新生成一个Page的实例,以前在Page上做的一些操作会消失。这不是我想要的,我希望有一个类似于PagePool的东西来缓存我这些创建好的页面,这样在切换的时候会减少创建实例的开销,同时可以保证无论怎么切换始终是同一个Page的实例。于是我做了如下的更改:
<ListBox x:Name="ListBox1"
ItemsSource="{Binding Source={StaticResource ListSource}}"
DisplayMemberPath="@Name"/>
<Frame x:Name="PageHost1"
NavigationUIVisibility="Hidden"
Content="{Binding ElementName=ListBox1, Path=SelectedItem, Converter={StaticResource Converter}}"/>
我直接把Frame的Content属性绑定到选中的项上,并且用一个Converter来返回Page的实例,在Converter里面,我用一个Dictionary来保存创建过的页面,保证它们只会被创建一次:
public class FrameContentConverter : IValueConverter

{
// 使用Page池来减少创建Page的开销
private Dictionary<string, Page> _pagePool = new Dictionary<string, Page>();


IValueConverter Members#region IValueConverter Members

public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

{
// 把Frame的Content绑定到ListBox的SelectedItem上,无法对变化做出响应
XmlElement node = value as XmlElement;
if (node != null)

{
string name = node.Attributes["Name"].Value;
string uri = node.Attributes["Uri"].Value;

if (this._pagePool.ContainsKey(name))

{
return this._pagePool[name];
}
else

{
Page page = Application.LoadComponent(new Uri(uri, UriKind.Relative)) as Page;
this._pagePool.Add(name, page);
return page;
}
}
return null;
}

public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)

{
XmlElement node = value as XmlElement;
if (node != null)

{
string name = node.Attributes["Name"].Value;
string uri = node.Attributes["Uri"].Value;

if (this._pagePool.ContainsKey(name))

{
return this._pagePool[name];
}
else

{
Page page = Application.LoadComponent(new Uri(uri, UriKind.Relative)) as Page;
this._pagePool.Add(name, page);
return page;
}
}
return null;
}

#endregion
}
理论上来说,这应该工作良好,然而,实际上却发生一点小小的意外:

Frame展示的内容只跟第一次选中的项有关,其后无论如何切换,Frame的内容均不受影响
难道是Frame的Content属性默认是以OneTime的模式来绑定的?于是我显示地指定Mode=OneWay,结果依然如此。
莫非Frame的Content属性只能指定一次?好吧,既然正着来不行,那我就试试反着的。
索性,我把ListBox的SeletedItem使用OneWayToSource的模式绑定到Frame的Content属性上。当然,这种情况下,FrameContentConverter里面的ConvertBack方法需要实现。
<ListBox x:Name="ListBox2"
ItemsSource="{Binding Source={StaticResource ListSource}}"
DisplayMemberPath="@Name"
SelectedItem="{Binding ElementName=PageHost2, Path=Content, Converter={StaticResource Converter}, Mode=OneWayToSource}"/>
<Frame x:Name="PageHost2" NavigationUIVisibility="Hidden"/>
采用这种方式后,一切正常了:


这说明,Frame的Content属性不是只能设置一次的,只是在对Frame的Content属性做绑定时,由于某种未知的原因,无法对数据源的变化产生响应,鉴于我目前的系统是Vista SP1,上次的Frame出现了渲染上的问题,这次我也没有找没装SP1的机子做测试,所以我并不能确定这是WPF本身的BUG还是sp1引起的问题,还是我某个地方没有弄好造成的。
做了一个对比的示例,有兴趣的读者可以下载回去看看。http://files.cnblogs.com/RMay/FrameContent.rar

posted @
2008-07-28 10:39 智者千虑 阅读(986) |
评论 (2) |
编辑

2008年7月22日
这几天一直有人问我如何保存RichTextBox的文本到数据库,包括格式等等,然后需要的再从数据库取出来,并且显示到RichTextBox中。
其实,RichTextBox的文本是一个FlowDocument类型的对象,我们只需要利用XamlReader和XamlWriter就能很好的完成上述工作。
【保存Document到流】
FlowDocument document = richTextBox.Document;
Stream s = new MemoryStream(); // 其他的什么Stream类型都没问题
XamlWriter.Save(document, s);
// 拿到s之后,再转化成二进制数据写到数据库就OK了
byte[] data = new byte[s.Length];
s.Position = 0;
s.Read(byte, 0, s.Length);
s.Close();
// 拿着data干啥都行
// ……
【从数据库中读取】
// data是从数据库中读出来的二进制数据
Stream s = new MemoryStream(data);
FlowDocument doc = XamlReader.Load(s) as FlowDocument;
s.Close();
richTextBox.Document = doc;
PS:有人问过我如何对RichTextBox的Document属性做绑定,由于RichTextBox的Document属性不是一个DependencyProperty,
所以我采用的是继承RichTextBox,自己定义一个BindableDocument的DependencyProperty来做。
public class BindableRichTextBox : RichTextBox

{
public FlowDocument BindableDocument

{

get
{ return (FlowDocument)GetValue(TextProperty); }

set
{ SetValue(TextProperty, value); }
}

// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("BindableDocument", typeof(FlowDocument), typeof(BindableRichTextBox), new UIPropertyMetadata(null, new PropertyChangedCallback(OnTextPropertyChanged)));

private static void OnTextPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)

{
BindableRichTextBox textBox = sender as BindableRichTextBox;
if (textBox != null)

{
textBox._changeFromBinding = true;
textBox.OnTextPropertyChanged(e);
}
}

// 防止死锁,比如A变了通知B,B变了又通知A
private bool _changeFromBinding = false;

// 当BindableDocument属性变化时,通知Document属性
protected virtual void OnTextPropertyChanged(DependencyPropertyChangedEventArgs e)

{
if (_changeFromBinding)

{
this.Document = e.NewValue as FlowDocument;
}
}

// 当Document属性变化时,通知BindableDocument属性
protected override void OnTextChanged(TextChangedEventArgs e)

{
base.OnTextChanged(e);
if (!_changeFromBinding)

{
this.BindableDocument = this.Document;
}
// 放到外面
_changeFromBinding = false;
}
}
做了个小程序,绑定了一个TextBox的Text到RichTextBox。 下载

posted @
2008-07-22 17:33 智者千虑 阅读(1102) |
评论 (1) |
编辑

2008年7月1日
|
Command
|
Name
|
Description
|
|
F i
|
FillRule
|
i=0:EvenOdd.
i=1:NonZero.
|
|
M x y
|
Move
|
Moveto (x, y).
|
|
m x y
|
Relative move
|
Moveto (x0+x, y0+y).
|
|
L x y
|
Line
|
Drawline to (x, y).
|
|
l x y
|
Relative line
|
Drawline to (x0+x, y0+y).
|
|
H x
|
Horizontal line
|
Drawline to (x, y0).
|
|
h x
|
Relative horizontal line
|
Drawline to (x0+x, y0).
|
|
V y
|
Vertical line
|
Drawline to (x0, y).
|
|
v y
|
Relative vertical line
|
Drawline to (x0, y0+y).
|
|
A xr yr a i j x y
|
Arc
|
Drawarc to (x, y) based on ellipse with radii (xr,yr) rotated a degrees. i=1:IsLargeArc. j=1: Clockwise.
|
|
a xr yr a i j x y
|
Relative arc
|
Drawarc to (x0+x, y0+y).
|
|
C x1 y1 x2y2 x3 y3
|
CubicBézier
|
DrawBézier to (x3, y3) withcontrol points (x1, y1) and (x2, y2).
|
|
c x1 y1 x2y2 x3 y3
|
Relative cubic Bézier
|
DrawBézier to (x0+x3,y0+y3) with control points (x0+x1, y0+y1) and (x0+x2, y0+y2).
|
|
S x2 y2 x3 y3
|
Smooth cubic Bézier
|
DrawBézier to (x3, y3) with reflected control point and (x2, y2).
|
|
s x2 y2 x3y3
|
Relative smooth cubic Bézier
|
DrawBézier to (x0+x3,y0+y3) with reflected control point and (x0+x2, y0+y2).
|
|
Q x1 y1 x2y2
|
Quadratic Bézier
|
Drawquadratic Bézier to (x2, y2)with control point (x1, y1).
|
|
q x1 y1 x2y2
|
Relative quadratic Bézier
|
Drawquadratic Bézier to (x0+x2,y0+y2) with control point(x0+x1, y0+y1).
|
|
Z
z
|
Closefigure
|
|
From: 《Application = Code + Markup : A Guide to the Microsoft Windows Presentation Foundation》 by Charles Petzold
posted @
2008-07-01 15:02 智者千虑 阅读(59) |
评论 (0) |
编辑

2008年6月30日
MeshGeometry3D的Normals属性和TextureCoordinates属性用于指定纹理如何贴到3D模型的表面去。
<MeshGeometry3D Positions="1,1,0 -1,1,0 -1,-1,0 1,-1,0"
Normals="0 0 1, 0 0 1, 0 0 1, 0 0 1"
TextureCoordinates="1 0, 0 0, 0 1, 1 1"
TriangleIndices="0,1,3 3,1,2"/>
上面的代码建立了一个简单的平面模型。

MSDN:Normal vectors are vectors perpendicular to the face of each triangle that defines a mesh. Normals determine whether a given triangle face is lit.(Normals决定了一个给定的三角面是否被照亮)
MSDN:Texture coordinates determine how a Material is mapped to the vertices of the triangles that make up a mesh. (TextureCoordinates决定了材质如何被映射到构成网格的三角形的顶点)
对于一个材质来说,它的顶点定义如下:

上面的代码: TextureCoordinates="1 0, 0 0, 0 1, 1 1"也就是说,将M1映射