I have a question on WPF dynamic positioning.
我有一个关于WPF动态定位的问题。
I want to place Elipses on the screen based on X and Y co-ordinates that i have stored in a collection in C#.
我想在基于X和Y坐标的屏幕上放置elips,这是我在c#中存储的。
I have been made aware of the drawing ability in WPF which you do from C# using the Windows.Media and Windows.Shapes.
我已经意识到WPF中的绘图能力,您可以使用Windows从c#中进行绘图。媒体和Windows.Shapes。
Now what i actually want to do is use these namespaces to draw the elipses in the first case in a canvas all done in c# using my datasource that i have in c# to position the elipses using the x and y co-ordinates.
现在我真正想做的是使用这些名称空间在画布中绘制省略号,这些省略号都是在c#中使用我在c#中拥有的数据源来使用x和y坐标定位省略号。
Now the complex part which is confusing me is what if the data in the datasource is changed as the data in the database changes, i will implement some sort of routine that checks the database every few seconds pulling back back any data that has changed since the last retrieval. Now i have seen the IPropertyChanged interface which i will inhert from for my class that i expose as my datasource for the page so when i retrieve the updated dataset i can call the PropertyChanged event which will notify WPF that the datasource has changed.
现在困惑我的复杂的部分就是如果数据源中的数据改变了数据库中的数据变化,我将实现某种常规检查数据库每隔几秒就撤回任何改变了自从上次检索的数据。现在我已经看到了IPropertyChanged接口,我将从这个接口继承到我的类,我将它作为页面的数据源公开,所以当我检索更新的数据集时,我可以调用PropertyChanged事件,它将通知WPF数据源已经更改。
How would i bind the elipses in the UI when i was laying them out originally in C# to certain items from the datasource so when the datasource changed the elipses would automatically change as required to reflect the changed datasource as long as the ID for each x and y co-ordinate remained the same. So can i bind to specific rows from the collection for each elipse in my canvas when i'm setting them out?
我怎么绑定椭圆在UI中当我躺他们最初在c#中某些项目从数据源当数据源改变了椭圆将自动根据需要改变,以反映更改数据源,只要每个x和y的ID协调保持不变。那么,我是否可以在设置画布中的每个elipse时,将集合中的特定行绑定到它们?
I don't even know if its possible to bind a datasource to a Canvas inside which i can use the collection as i require to begin with but i thought i'd put this question out there incase someone has done something similar so i have a good starting point.
我甚至不知道是否可以将一个数据源绑定到一个画布里面,我可以使用这个集合作为开始,但是我想我应该把这个问题放在这里,因为有人做了类似的事情,所以我有一个很好的起点。
Thanks Iffy.
由于不确定的。
3 个解决方案
#1
1
To build on what others have said here is a complete self contained example - you can copy it straight into kaxaml or xamlpad (or blend, but I think in that case it has to go into a body of a usercontrol or a window) and see how it works.
要在其他人所说的基础上构建一个完整的自包含示例——您可以直接将它复制到kaxaml或xamlpad(或blend)中(但我认为在这种情况下,它必须进入usercontrol或窗口的主体)并查看它是如何工作的。
Instead of using the rendertransform I prefer to use the canvas and set the left and top property, I just find it more readable that way. Alternatively, you can use a grid and set the margin but then you'll need a value converter of some kind.
与使用rendertransform不同,我更喜欢使用画布并设置左和上属性,我只是觉得这样更可读。或者,您可以使用网格并设置页边距,但是您需要某种值转换器。
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid.Resources>
<!-- This is our list of shapes, in this case an inline XML list -->
<XmlDataProvider x:Key="ShapeList">
<x:XData>
<ObjectList xmlns="">
<Shapes>
<shape height="30" width="30" x="50" y="50"/>
<shape height="30" width="40" x="100" y="100"/>
<shape height="30" width="50" x="150" y="150"/>
<shape height="30" width="60" x="200" y="200"/>
<shape height="30" width="70" x="250" y="350"/>
</Shapes>
</ObjectList>
</x:XData>
</XmlDataProvider>
</Grid.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource ShapeList}, XPath=ObjectList/Shapes/*}">
<!-- this template sets the panel as canvas for easy positioning -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- this template defines how each bound item is represented -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="{Binding XPath=@width}" Height="{Binding XPath=@height}">
<Ellipse Fill="White" Stroke="Black" StrokeThickness="2"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!-- This style positions each bound item's container -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding XPath=@x}"/>
<Setter Property="Canvas.Top" Value="{Binding XPath=@y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
Instead of binding to an inline xml list you can bind to a collection on your viewmodel (best choice), a dependency property on your control or window, set the resource from codebehind, etc.
与绑定到内联xml列表不同,您可以绑定到viewmodel上的集合(最佳选择)、控件或窗口上的依赖属性、从codebehind设置资源等等。
The key point is that you shouldn't be laying out the ellipses in C# unless you absolutely have to. Provide the data as some sort of a list of meaningful objects. Then create a data template that defines how that data is represented. Assuming you don't have to do any sort of complicated processing of your object to get the relevant ellipse properties you should be able to do this without any code, or at most with a few value converters.
关键是你不应该在c#中列出省略号,除非你必须这样做。将数据作为有意义对象的列表提供。然后创建一个数据模板,定义如何表示数据。假设您不需要对对象进行任何复杂的处理来获得相关的椭圆属性,那么您应该可以在不使用任何代码的情况下,或者最多使用一些值转换器。
This is the sort of UI separation that allows you to deal with updating the datasource (business logic) and displaying items (ui) separately.
这是一种UI分离,允许您处理更新数据源(业务逻辑)和分别显示项目(UI)。
So basically the idea is:
基本上我们的想法是:
- Expose a collection of objects - in my example this would be a collection of classes mirroring the structure of the shape xml element in the list. This can be the business object itself, or a viewmodel - a class that wraps a business objects and exposes conveniently bindable properties (in this case, position and size). The collection itself would prefereably be an ObservableCollection, so that UI is notified when you add or remove objects. Toss in some design time data into it if possible.
- 公开一个对象集合——在我的示例中,这是一个类集合,它反映了列表中形状xml元素的结构。这可以是业务对象本身,也可以是viewmodel——一个封装业务对象并公开可绑定属性(在本例中是位置和大小)的类。集合本身最好是一个观察汇总,以便在添加或删除对象时通知UI。如果可能的话,将一些设计时间数据放入其中。
- Bind to the collection, using the WPF datatemplates to define how an element should be presented. In this case I used a plain ItemsControl with a few simple templates, but this can be as complex as required
- 绑定到集合,使用WPF datatemplates定义应该如何显示元素。在本例中,我使用了一个普通的ItemsControl和一些简单的模板,但是这可以是所需的复杂程度
- Work out how the collection will be updated from the original datasource. If you set up the previous steps correctly this is essentially a separate problem
- 计算如何从原始数据源更新集合。如果您正确地设置了前面的步骤,这实际上是一个单独的问题。
#2
0
You can use a translate transform to position the ellipses as you create them.
您可以在创建椭圆时使用转换来定位它们。
TranslateTransform transform = new TranslateTransform();
transform.X = X;
transform.Y = Y;
Ellipse ellipse = new Ellipse();
ellipse.RenderTransform = transform;
...
You could store the ellipses in a dictionary with the id as they key for quick and easy retrieval.
您可以将省略号存储在带有id的字典中,作为快速和容易检索的键。
TranslateTransform transform = data[id].RenderTransform as TranslateTransform;
transform.X = newX;
transform.Y = newY;
#3
0
You can accomplish this within a DataTemplate if your Ellipse objects are represented by a class, and perhaps displayed in an ItemsControl.
如果椭圆对象由类表示,并且可能显示在ItemsControl中,那么可以在DataTemplate中实现这一点。
<Ellipse>
<Ellipse.LayoutTransform>
<TranslateTransform X="{Binding XCoord}"
Y="{Binding YCoord}" />
</Ellipse.LayoutTransform>
</Ellipse>
You would choose between LayoutTransform and RenderTransform based on the panel which held your Ellipse objects.
您可以根据存放椭圆对象的面板在LayoutTransform和RenderTransform之间进行选择。
I also recommend reviewing an article by Bea Stollnitz (neé Costa) which shows how to leverage a ListBox backed by a Canvas with DataBinding to produce offset objects. Very cool.
我还建议您阅读Bea Stollnitz (nee Costa)的一篇文章,该文章展示了如何利用由具有数据库的画布支持的列表框来生成偏移对象。非常酷。
#1
1
To build on what others have said here is a complete self contained example - you can copy it straight into kaxaml or xamlpad (or blend, but I think in that case it has to go into a body of a usercontrol or a window) and see how it works.
要在其他人所说的基础上构建一个完整的自包含示例——您可以直接将它复制到kaxaml或xamlpad(或blend)中(但我认为在这种情况下,它必须进入usercontrol或窗口的主体)并查看它是如何工作的。
Instead of using the rendertransform I prefer to use the canvas and set the left and top property, I just find it more readable that way. Alternatively, you can use a grid and set the margin but then you'll need a value converter of some kind.
与使用rendertransform不同,我更喜欢使用画布并设置左和上属性,我只是觉得这样更可读。或者,您可以使用网格并设置页边距,但是您需要某种值转换器。
<Grid xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Grid.Resources>
<!-- This is our list of shapes, in this case an inline XML list -->
<XmlDataProvider x:Key="ShapeList">
<x:XData>
<ObjectList xmlns="">
<Shapes>
<shape height="30" width="30" x="50" y="50"/>
<shape height="30" width="40" x="100" y="100"/>
<shape height="30" width="50" x="150" y="150"/>
<shape height="30" width="60" x="200" y="200"/>
<shape height="30" width="70" x="250" y="350"/>
</Shapes>
</ObjectList>
</x:XData>
</XmlDataProvider>
</Grid.Resources>
<ItemsControl ItemsSource="{Binding Source={StaticResource ShapeList}, XPath=ObjectList/Shapes/*}">
<!-- this template sets the panel as canvas for easy positioning -->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas IsItemsHost="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!-- this template defines how each bound item is represented -->
<ItemsControl.ItemTemplate>
<DataTemplate>
<Border Width="{Binding XPath=@width}" Height="{Binding XPath=@height}">
<Ellipse Fill="White" Stroke="Black" StrokeThickness="2"/>
</Border>
</DataTemplate>
</ItemsControl.ItemTemplate>
<!-- This style positions each bound item's container -->
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Left" Value="{Binding XPath=@x}"/>
<Setter Property="Canvas.Top" Value="{Binding XPath=@y}"/>
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Grid>
Instead of binding to an inline xml list you can bind to a collection on your viewmodel (best choice), a dependency property on your control or window, set the resource from codebehind, etc.
与绑定到内联xml列表不同,您可以绑定到viewmodel上的集合(最佳选择)、控件或窗口上的依赖属性、从codebehind设置资源等等。
The key point is that you shouldn't be laying out the ellipses in C# unless you absolutely have to. Provide the data as some sort of a list of meaningful objects. Then create a data template that defines how that data is represented. Assuming you don't have to do any sort of complicated processing of your object to get the relevant ellipse properties you should be able to do this without any code, or at most with a few value converters.
关键是你不应该在c#中列出省略号,除非你必须这样做。将数据作为有意义对象的列表提供。然后创建一个数据模板,定义如何表示数据。假设您不需要对对象进行任何复杂的处理来获得相关的椭圆属性,那么您应该可以在不使用任何代码的情况下,或者最多使用一些值转换器。
This is the sort of UI separation that allows you to deal with updating the datasource (business logic) and displaying items (ui) separately.
这是一种UI分离,允许您处理更新数据源(业务逻辑)和分别显示项目(UI)。
So basically the idea is:
基本上我们的想法是:
- Expose a collection of objects - in my example this would be a collection of classes mirroring the structure of the shape xml element in the list. This can be the business object itself, or a viewmodel - a class that wraps a business objects and exposes conveniently bindable properties (in this case, position and size). The collection itself would prefereably be an ObservableCollection, so that UI is notified when you add or remove objects. Toss in some design time data into it if possible.
- 公开一个对象集合——在我的示例中,这是一个类集合,它反映了列表中形状xml元素的结构。这可以是业务对象本身,也可以是viewmodel——一个封装业务对象并公开可绑定属性(在本例中是位置和大小)的类。集合本身最好是一个观察汇总,以便在添加或删除对象时通知UI。如果可能的话,将一些设计时间数据放入其中。
- Bind to the collection, using the WPF datatemplates to define how an element should be presented. In this case I used a plain ItemsControl with a few simple templates, but this can be as complex as required
- 绑定到集合,使用WPF datatemplates定义应该如何显示元素。在本例中,我使用了一个普通的ItemsControl和一些简单的模板,但是这可以是所需的复杂程度
- Work out how the collection will be updated from the original datasource. If you set up the previous steps correctly this is essentially a separate problem
- 计算如何从原始数据源更新集合。如果您正确地设置了前面的步骤,这实际上是一个单独的问题。
#2
0
You can use a translate transform to position the ellipses as you create them.
您可以在创建椭圆时使用转换来定位它们。
TranslateTransform transform = new TranslateTransform();
transform.X = X;
transform.Y = Y;
Ellipse ellipse = new Ellipse();
ellipse.RenderTransform = transform;
...
You could store the ellipses in a dictionary with the id as they key for quick and easy retrieval.
您可以将省略号存储在带有id的字典中,作为快速和容易检索的键。
TranslateTransform transform = data[id].RenderTransform as TranslateTransform;
transform.X = newX;
transform.Y = newY;
#3
0
You can accomplish this within a DataTemplate if your Ellipse objects are represented by a class, and perhaps displayed in an ItemsControl.
如果椭圆对象由类表示,并且可能显示在ItemsControl中,那么可以在DataTemplate中实现这一点。
<Ellipse>
<Ellipse.LayoutTransform>
<TranslateTransform X="{Binding XCoord}"
Y="{Binding YCoord}" />
</Ellipse.LayoutTransform>
</Ellipse>
You would choose between LayoutTransform and RenderTransform based on the panel which held your Ellipse objects.
您可以根据存放椭圆对象的面板在LayoutTransform和RenderTransform之间进行选择。
I also recommend reviewing an article by Bea Stollnitz (neé Costa) which shows how to leverage a ListBox backed by a Canvas with DataBinding to produce offset objects. Very cool.
我还建议您阅读Bea Stollnitz (nee Costa)的一篇文章,该文章展示了如何利用由具有数据库的画布支持的列表框来生成偏移对象。非常酷。