星期三, 七月 04, 2007
一首不错的歌曲
歌词
A ray of hope flick-ers in the sky,
a ti-ny star lights up way up high.
All a-cross thee land,
dawns a brand new morn.
This comes to pass,
when a child is born.
[A si-lent wish sails the sev-en seas,
the wind of change whisp-er in the trees.
And the walls of doubt,
crum ble tost and torn.
This comes to pass,
when a child is born.
[A ro-sy hill set-tles all a-round.
You got to feel you're on so-lid ground.
For a spell or two no one seems for-lorn.
This comes to pass when a child is born.
[It's all a dream,
an ill-u-sion now,
it must come true some-time soon some how.
All a-cross the land,
dawns a brand new morn.
This comes to pass,
when a child is born.
星期二, 四月 17, 2007
星期二, 一月 09, 2007
Master-detail情形
Master-detail情形
原文出处:http://www.beacosta.com/Archive/2005_11_01_bcosta_archive.html最简单的Master-detail情形就是,点击ItemsControl特定,使该项的细节在另一个控件中显示出来.例如,一个程序在ListBox 中显示了一个顾客名字列表,当点击了一个特定的顾客时,TextBlock就更新这个顾客的地址,电话号码和出生日期.
在这个帖子中我将使用太阳系的行星作为数据源:点击ListBox中的行星名字,使该行星的图片与信息显示在被模板化的ContentControl上.ListBox扮演master的角色,ContentControl呈现细节.
在Window的资源段落,有一个提供行星数据的XmlDataProvider和一个CollectionViewSource,该 CollectionViewSource绑定Source属性到这个provider(要获得更多关于CollectionViewSource的信息,可以参考我前面的帖子.)这里是ListBox绑定到CollectionViewSource的markup:
<!-- master -->
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="@Name" Padding="5" Margin="0,0,5,0"/>
我还需要一个ContentControl来显示选择项的细节.下面的markup看起来有点奇怪:我们绑定ContentControl(只能显示单项)到很多项的集合?(注意看它的绑定和ListBox的itemsSource的绑定很像).这个markup工作的很好,因为数据绑定引擎足够聪明,它能分辨出这两个目标.当绑定ItemsControl到数据集合,我们能得到整个集合, 当我们绑定ContentControl到数据集合,我们得到的是集合的当前项.这就是在wpf中实现Marster-detail如此简单的缘故了.
<!-- detail -->
<ContentControl ContentTemplate="{StaticResource detailTemplate}" Content="{Binding Source={StaticResource cvs}}"/>
为了详细说明如何在ContentControl中显示行星的细节数据,我们使用了DataTemplate.下面的markup显示了 DataTemplate的数据绑定部分.注意,因为我们是绑定到XML,因此绑定使用XPath而不是Path.
<DataTemplate x:Key="detailTemplate">
(...)
<Image Source="{Binding XPath=Image, Converter={StaticResource stringToImageSource}}" />
(...)
<StackPanel Orientation="Horizontal" Margin="5,5,5,0">
<TextBlock Text="Orbit: " FontWeight="Bold" />
<TextBlock Text="{Binding XPath=Orbit}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5,0,5,0">
<TextBlock Text="Diameter: " FontWeight="Bold"/>
<TextBlock Text="{Binding XPath=Diameter}" />
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="5,0,5,5">
<TextBlock Text="Mass: " FontWeight="Bold"/>
<TextBlock Text="{Binding XPath=Mass}" />
</StackPanel>
(...)
</DataTemplate>
这里就是该示例的截图:
星期五, 一月 05, 2007
如何同步两个绑定数据的ListBox的选择项?
如何同步两个绑定数据的ListBox的选择项?
原文出处:http://www.beacosta.com/Archive/2005_11_01_bcosta_archive.html 我将会向你展示两种方法来同步绑定数据的ListBox的选择项。
在第一种解决方案中,我将会创建数据集合的定制view,然后把两个ListBox都绑定到它上面。view能够跟踪它背后数据集合的当前项,允许我们排序,组合,过滤它的数据项。CollectionViewSource是九月CTP发布的一个新的类,能够用标识来创建一个定制的view。因为在这个例子中,创建的定制的view所跟踪数据集合的当前项,currency,选择项都是同步的,因此绑定两个ListBox到相同的view能够让他们的选择项同步。
<Window.Resources>
<local:GreekGods x:Key="source" />
<CollectionViewSource Source="{StaticResource source}" x:Key="cvs"/>
</Window.Resources>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name"/>
<ListBox ItemsSource="{Binding Source={StaticResource cvs}}" DisplayMemberPath="Name"/>
我将会在以后的帖子中介绍如何使用CollectionViewSource来排序,分组,和过滤数据项。
另一个方法也能达到同样的效果,就是设置ItemsSource属性为数据源,同时设置IsSynchronizedWithCurrentItem属性为true:
<ListBox ItemsSource="{StaticResource source}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
<ListBox ItemsSource="{StaticResource source}" IsSynchronizedWithCurrentItem="True" DisplayMemberPath="Name"/>
这个markup有此功能是因为当绑定到一个集合时总是会创建一个view,如果你没有指定一个view,系统就会创建一个默认的view。虽然这个 view和定制的view用同样的方式跟踪数据,但如果你有默认的view,currency和选择项是默认不同步的。改变这个默认的行为的方法就是设置 IsSynchroizedWithCurrentItem属性为true
这些数据组通过定制的回馈来区分定制views和默认views之间的默认同步方式的不同之处。这种方法能让玩家了解到view的概念,且能够根据计自己的需要精确的指定哪些能够同步,哪些不能。
下面的图片中,第一和第二个ListBox是绑定到CollectionViewSource,第三第四个设置 InSynchronizedWithCurrentItem为true.
星期四, 十二月 28, 2006
拖拽WPF元件
拖拽WPF元件
原文出处:http://blogs.msdn.com/marcelolr/archive/2006/03/02/542641.aspx 这是介绍心爱的UI gesture(dragging和dropping)系列的第一篇帖子
在这第一篇的帖子中,我们会考察最简单的,最直接的方法:增加一系列的窗口事件处理器来更新布局属性.我将会使用VB.NET-转换到C#应该不是很困难的.(译者:因为不懂vb,代码会直接翻译成c#)
第一,我们从window1.xaml的代码开始.
<Window x:Class="SimpleDrapElement.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="SimpleDrapElement" Height="300" Width="300"
>
<DockPanel>
<Border DockPanel.Dock="Top" BorderBrush="DarkGray" BorderThickness="2" Padding="4">
<TextBlock FontSize="8pt" FontFamily="Tahoma" TextWrapping="Wrap">
<Bold>
Drag Canvas Sample
</Bold>
<LineBreak />
<Run>
This sample demonstrates using adorners to drag shapes (or any other element).
</Run>
</TextBlock>
</Border>
<Canvas Name="MyCanvas">
<Canvas.Background>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop Color="White" Offset="0" />
<GradientStop Color="DarkBlue" Offset="1" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Canvas.Background>
<TextBox Canvas.Top="120" Canvas.Left="8" />
<Rectangle Canvas.Top="8" Canvas.Left="8"
Width="32" Height="32" Fill="LightPink" />
<Ellipse Canvas.Top="8" Canvas.Left="48"
Width="40" Height="16" Fill="Tan" />
<Rectangle Canvas.Top="48" Canvas.Left="8"
Width="32" Height="62" Fill="Green" />
<Ellipse Canvas.Top="48" Canvas.Left="48"
Width="62" Height="62" Fill="Orange" />
</Canvas>
</DockPanel>
</Window>
创建了一个简单的窗口,看起来就象这样:
好了,现在让我们挽起袖子,一点一点的往window1.xaml.cs里添加代码.
首先,从window1的constructor正下方开始,我们增加一些代码.
private Point m_StartPoint;// Where did the mouse start off from?
private Double m_OriginalLeft;// What was the element;//s original x offset?
private Double m_OriginalTop;// What was the element;//s original y offset?
private Boolean m_IsDown;// Is the mouse down right now?
private Boolean m_IsDragging;// Are we actually dragging the shape around?
private UIElement m_OriginalElement;// What is it that we;//re dragging?
private Rectangle m_OverlayElement;// What is it that we;//re using to show where the shape will end up?
这就是我们所要探知的如何在Canvas上移动shapes的信息.
{
if (e.Source == MyCanvas)
{
return;
}
m_IsDown = true;
m_StartPoint = e.GetPosition(MyCanvas);
m_OriginalElement = e.Source as UIElement;
MyCanvas.CaptureMouse();
e.Handled = true;
}
private void MyCanvas_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (m_IsDown)
{
if (!m_IsDragging &&
Math.Abs(e.GetPosition(MyCanvas).X - m_StartPoint.X) > SystemParameters.MinimumHorizontalDragDistance &&
Math.Abs(e.GetPosition(MyCanvas).Y - m_StartPoint.Y) > SystemParameters.MinimumVerticalDragDistance)
{
DragStarted();
}
if (m_IsDragging)
{
DragMoved();
}
}
}
有几件事需要注意的:点击鼠标时我们获取了鼠标(万一用户把鼠标移动到边界外),和DrapStarted(),DragMoved()中使用的 helper方法.另外也必须要注意,考虑到用户的选择,我们使用系统参数的开始拖拽距离设置.这个对笔记本与平板电脑特别有用,因为使用鼠标棒和鼠标板时是很容易引起鼠标晃动的.
现在,要在我们的helper方法中做什么呢?
{
m_IsDragging = true;
m_OriginalLeft = Canvas.GetLeft(m_OriginalElement);
m_OriginalTop = Canvas.GetTop(m_OriginalElement);
VisualBrush brush = new VisualBrush(m_OriginalElement);
brush.Opacity = 0.5;
m_OverlayElement = new Rectangle();
m_OverlayElement.Width = m_OriginalElement.RenderSize.Width;
m_OverlayElement.Height = m_OriginalElement.RenderSize.Height;
m_OverlayElement.Fill = brush;
MyCanvas.Children.Add(m_OverlayElement);
}
private void DragMoved()
{
Point currentPosition = Mouse.GetPosition(MyCanvas);
double elementLeft = currentPosition.X - m_StartPoint.X + m_OriginalLeft;
double elementTop = currentPosition.Y - m_StartPoint.Y + m_OriginalTop;
Canvas.SetLeft(m_OverlayElement, elementLeft);
Canvas.SetTop(m_OverlayElement, elementTop);
}
注意我是如何使用透明的VisulaBrush来显示最终的shape.另外,由于我们使用的是Canvas,所以只要改变Left和top属性.如果是 DockPanel,我将会尝试是否呢个能够snapping边界,或者如果是Grid,我将会试着指出shape要放下的格子.我只是研究了最简单的一个例子,你们自己可以继续深入.
这已经很好了,但我们如何放下shape呢?有两种典型做法,一个是释放鼠标按钮,另一个是按下esc键.让我们来编写它:
private void MyCanvas_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (m_IsDown)
{
DragFinished(false);
e.Handled = true;
}
}
private void Window1_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape && m_IsDragging)
{
DragFinished(true);
}
}
另外,一个helper方法很明了的显示了当我们停止拖拽时都发生了什么,这里是它的实现:
{
Mouse.Capture(null);
if (m_IsDragging)
{
MyCanvas.Children.Remove(m_OverlayElement);
if (!canceled)
{
Canvas.SetLeft(m_OriginalElement, Canvas.GetLeft(m_OverlayElement));
Canvas.SetTop(m_OriginalElement, Canvas.GetTop(m_OverlayElement));
}
m_OverlayElement = null;
}
m_IsDragging = false;
m_IsDown = false;
}
我亲爱的读者们.今天的例子结束了.试着实现,扩展一下他们,惊讶于他们半透明的预览.你能否添加其它的控件来拖拽呢?
(以下非翻译内容)
由于改了c#代码,所以要在Window1的构造函数要修改为:
public Window1()
{
InitializeComponent();
MyCanvas.PreviewMouseLeftButtonDown += MyCanvas_PreviewMouseLeftButtonDown;
MyCanvas.PreviewMouseMove += MyCanvas_PreviewMouseMove;
MyCanvas.PreviewMouseLeftButtonUp += MyCanvas_PreviewMouseLeftButtonUp;
this.PreviewKeyDown += Window1_PreviewKeyDown;
}
代码下载:
share your files at box.net
星期三, 十二月 27, 2006
如何实现条形图的数据绑定
如何实现条形图的数据绑定
原文出处:http://www.beacosta.com/Archive/2005_10_01_bcosta_archive.html
Avalon的风格和模板特征与绑定数据的ItemsControl结合可以创建一个简单的条形图.ItemsControl是一个用来显示一系列项的控件.这些项可以是你想要的任何东西:人,数字,控件等等.如果一个ItemsControl的每一项的都可以用矩形来表示,且矩形的高度绑定到数字的数据,你就可以得到一个绑定数据的条形图.
这个例子的数据源是一个带ValueCollection属性的类,ValueCollection的类型为ObservableCollection<int>.ObservableCollection<T>继承自 INotifyCollectionChanged,INotifyCollectionChanged表示当从集合增加/移除/覆盖项时, 数据绑定引擎将会通知这些变化,界面将会得到更新.
这里是ItemsControl的表示法:
<ItemsControl ItemsSource="{Binding Source={StaticResource source}, Path=ValueCollection}"
ItemTemplate="{StaticResource template}" Height="130">
<ItemsControl.ItemsPanel>
<StackPanel Orientation="Horizontal" />
</ItemsControl.ItemsPanel>
</ItemsControl>
ItemsControl的默认Panel是垂直方向的,但是我们想要各项按照水平方向显示-每一个条形都在前一个的右边.为了改变Panel,我们修改了ItemsControl的ItemsPanel属性(关于如何改变ItemsControl的Panel的细节请察看我的上一片帖子).< br/>
每项的模板包含一个高度绑定到相应整数数据源的矩形,和一个没有填充的矩形来增加条形之间的间隔:
<DataTemplate x:Key="template">
<StackPanel Orientation="Horizontal" VerticalAlignment="Bottom">
<Rectangle Height="{Binding}" Width="20" Fill="Red" />
<Rectangle Width="5" />
</StackPanel>
</DataTemplate>
这是一个很简单的例子,但是它将带给你一些思路,是处理更复杂数据的基础.结果表现如下:
如何改变ItemsControl的布局
如何改变ItemsControl的布局
原文出处:http://www.beacosta.com/Archive/2005_10_01_bcosta_archive.html
在这个例子中,我将会介绍两个方法来改变ItemsControl的布局.为了回答我前面帖子的评论,这个例子使用XmlDataProvider,它可以绑定到XML数据.
改变ItemsControl布局的最简单的方法就是简单的设置ItemsPanel属性为你想要保存条目的Panel
<ListBox ItemsSource="{Binding Source={StaticResource xmlData}}" (...) >
<ListBox.ItemsPanel>
<StackPanel Orientation="Horizontal" />
</ListBox.ItemsPanel>
</ListBox>
另外就是创建一个ControlTemplate,它允许被更广泛的定制.ControlTemplate允许你用替换掉整个VisualTree,包括选择一个新的Panel来保存条目.例如,下面的标记就显示了一个ControlTemplate,它增加一个了Border且改变了 ItemsControl的Panel.
<ControlTemplate x:Key="listBoxTemplate">
<Border BorderBrush="Orange" BorderThickness="2" Margin="10,0,10,10">
<StackPanel Orientation="Horizontal" IsItemsHost="True" />
</Border>
</ControlTemplate>
<ListBox ItemsSource="{Binding Source={StaticResource xmlData}}" Template="{StaticResource listBoxTemplate}" (...) />
Most people get this far in this scenario,但是忘记了设置Panel的IsItemsHost的属性.IsItemHost指明"使用这个Panel来放置 ItemsControl中的条目".注意,选择还是可以照常使用的.
如果你想让条目能够自动换行,你可以使用WrapPanel代替StackPanel.在这种情形下,要牢记ListBox的默认template包含一个ScrollViewer,因此你的条目是不会自动换行的.为了让它们换行你可以设置你自己的ControlTemplate,如果你不需要选择功能, 可以使用ItemsControl替代ListBox.
我前面提过,我使用XmlDataProvider绑定XML数据.这是我如何转化前面例子中使用的GreekGods运行时数据源:< br/>
<Window.Resources>
<XmlDataProvider XPath="/GreekGods/GreekGod" x:Key="xmlData">
<GreekGods xmlns="">
<GreekGod>
<Name>Aphrodite</Name>
<Description>Goddess of love, beauty and fertility</Description>
<RomanName>Venus</RomanName>
</GreekGod>
(...)
</GreekGods>
</XmlDataProvider>
</Window.Resources>
你只需要记住在绑定对象中使用XPath属性代替Path属性.使用DisplayMemberPath格式时,Path和XPath都可以使用.
SelectedValue和SelectedItem有什么不同?
SelectedValue和SelectedItem有什么不同?
http://www.beacosta.com/Archive/2005_10_01_bcosta_archive.html
当他们被分别使用时,这两个属性是很相象的.但当同时使用且设置了SelectedValuePath时,不同之处就显示出来了.
例如,考虑到我们熟悉的GreekGods数据源,我通过代码设置StackPanel的DataContext为这个集合:
GreekGods items;
items = new GreekGods();
mainStackPanel.DataContext = items;
且使用空的Binding绑定到ListBox的集合上.我想通过"Messenger of the Gods"描述来选择GreekGod(即使我只显示每个God的名字).这时SelectedValuePath就很有用了.每个ListBox的项都是一个GreekGod物件. 通过设置SelectedValuePath为"Description",我能够深入到每个GreekGod的Description属性.这样我只要设置SelectedValue为我所想要的描述就可以选择该项了.
ItemsSource="{Binding}" DisplayMemberPath="Name"
SelectedValue="Messenger of the Gods" SelectedValuePath="Description"
Name="listBox1" (...) />
string messengerOfGods = (string)(listBox1.SelectedValue);
GreekGod hermes = (GreekGod)(listBox1.SelectedItem);
当只需要绑定模型上的部分数据时,SelectedValue就很有用了.你可以绑定SelectedValue属性到你模型的部分信息上,而ListBox却能显示更多的信息.
如果你知道如何结合这两个属性为一个,请告诉我们.
If you have ideas of how to combine these two properties in one, we would love to hear it.
星期二, 十二月 26, 2006
九月PDC新的特性:DisplayAMemberPath
九月PDC新的特性:DisplayAMemberPath
原文出处:http://www.beacosta.com/Archive/2005_10_01_bcosta_archive.html 就像我以前的帖子所表现的,绑定一个ItemsControl到一个IEnumerable数据源确实是很容易(记住ListBox和ComboBox继承自ItemsControl).当你想以文字形式显示每个数据项的单个属性时,利用九月PDC介绍的DisplayMemberPath,就更容易了.在DisplayMemberPath出现之前,这种情形需要使用到DataTemplate来指定我们所兴趣的属性.就像这样的xaml:
<Window.Resources>
<DataTemplate x:Key="itemTemplate">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</Window.Resources>
<ItemsControl ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}" />
数据绑定团队意识到这是一个非常常见的情形,需要简单化.这就是产生ItemsControl的DisplayMemberPath属性的缘故了.上面的这种情形现在可以用一行的xaml做到:
<ItemsControl ItemsSource="{StaticResource greekGods}" DisplayMemberPath="Name" />
很容易吧:)
下面的图片显示了两种版本的ItemsControl,左边的使用DataTemplate,右边的使用DisplayMemberPath.
以下不是翻译内容:
奇怪的是作者的例子显示出来的并不是这样图片.修改以下.:)
share your files at box.net
星期一, 十二月 25, 2006
如何绑定ComboBox的Items?如何从绑定的ComboBox中获取ComboBoxItem?
如何绑定ComboBox的Items?如何从绑定的ComboBox中获取ComboBoxItem?
原文出处:http://www.beacosta.com/Archive/2005_09_01_bcosta_archive.html
绑定ComboBox的Items和绑定ListBox的Items是很象的:
<Window.Resources>
<local:GreekGods x:Key="greekGods"/>
<DataTemplate x:Key="itemTemplate">
<TextBlock Text="{Binding Path=Name}" />
</DataTemplate>
</Window.Resources>
<ComboBox ItemsSource="{StaticResource greekGods}" ItemTemplate="{StaticResource itemTemplate}" Width="200" Name="comboBox"/>
相似的原因是因为ComboBox和ListBox都是从ItemsControl继承来的,且ItemsSource和ItemTemplate都是ItemsControl的属性.
如果你读过前面关于如果从绑定数据的ListBox中获取一个ListBoxItem的帖子,你或许会认为没有必要继续阅读如何为ComboBox做同样的事.但是那里还是有一些小窍门你必须知道的.
如果你使用与前面帖子相似的代码去解决问题,那就会发现ComboBoxItems是null:
GreekGod greekGod = (GreekGod)(comboBox.Items[0]);
ComboBoxItem cbi1 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromIndex(0));
ComboBoxItem cbi2 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromItem(comboBox.Items.CurrentItem));
这是因为ComboBox只会在你打开它时才会有产生的items.所以这个窍门就是当你调用ContainerFromIndex或ContainerFromItem前打开ComboBox:
GreekGod greekGod = (GreekGod)(comboBox.Items[0]);
comboBox.IsDropDownOpen = true;
ComboBoxItem cbi1 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromIndex(0));
ComboBoxItem cbi2 = (ComboBoxItem)(comboBox.ItemContainerGenerator.ContainerFromItem(comboBox.Items.CurrentItem));
comboBox.IsDropDownOpen = false;
由于原来作者的代码比较早期,vs更新后无法编译了.自己小小修改以下:
share your files at box.net