.Net WinForms设计同步数据和控件 [英] .Net WinForms design to sync Data and Controls

查看:105
本文介绍了.Net WinForms设计同步数据和控件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的任务是将VB6 楼层平面图应用程序转换为VB.Net



应用程序使用数百个动态创建在X和Y坐标(.Top,.Left)上放置和操作Form的图像或形状。



例如:poco数据类看起来像这样

  public class Appliance {
public int Id {get;组; }
public string Name {get;组; }
public int Top {get;组; }
public int Left {get;组; }
public int Width {get;组; }
public int Height {get;组; }
public int Type {get;组;
public int Color {get;组; }
public bool Visible {get;组; }
}

我开始在一个FloorPlan类中工作,其中包含POCO对象的列表,如这将代表图像或形状及其在表单上的位置。
在勾画出以下内容之后,我意识到我一定要做错了。

  //从DBFiles中填充DATA对象
列表< Appliance> applianceData = GetAppliancesFromDataFile()
列表< PictureBox> applianceUI =新列表< PictureBox>();

//创建一堆PictureBox控件
foreach(在设备中的变量设备){
Image img = GetApplianceImage(appliance);
Appliances.Add(new PictureBox {.Image = img})
appliancesUI.Controls.Add(img);
}

//将这些PictureBox控件添加到窗体(通过面板)
foreach(appliance pic中的var pic){
FormPanel.Controls.Add(pic) ;
}

我知道这是一个更好的方式来做到这一点。
我需要在我的类中的原始数据之间链接到添加到表单的实际图像控件。可能没有办法拥有2way数据绑定,但是必须比没有从PictureBox控件导出poco类更好的东西。



什么是最好的方式同步我的poco类中的数据之间的数据以及将被创建并添加到表单中的Form Image对象的属性,并停止这种疯狂?

解决方案

由于你是WPF的新手,我将一步步走上去,因此它可以得到一点点长的时间重复,但是忍受我。


注意:首先,当您开始研究WPF时,WPF可能看起来像一个复杂而令人信服的框架,但不是。实际上,只要



不是很多吗?别担心,我们刚刚开始。这里需要注意的一点是,在我们的UI 中有3个东西,它们对应于 List< Appliance> 中的3个项目我们的DataContext。



步骤3:



默认情况下,ItemsControl将垂直堆栈元素,我们想要什么相反,我们将要更改



步骤4:



现在我们得到了大小和定位权利,但是我们仍然得到这个丑陋的文本,而不是适用于我们的电器的视觉效果。这是



开始看起来更好,对?注意我们甚至没有编写一行代码到目前为止,这一切都是用20行声明性XAML和DataBinding完成的。



< h2>下一步:

所以,现在我们有基础知识,我们已经实现了保持 Appliance 类完全脱离了UI,但仍然有很多功能,我们可能希望包含在我们的应用程序中:




  • 通过点击它们来选择项目:这可以通过将 ItemsControl 更改为 ListBox (同时保持其属性完整)只需使用Ctrl + H。由于 ListBox 实际上源于 ItemsControl ,所以我们可以使用到目前为止写的XAML。

  • 启用点击拖动:这可以通过在 DataTemplate 中放置 Thumb / code>并处理其 DragDelta 事件。

  • 启用双向数据绑定:此将允许您修改 Appliance 类中的属性,并使WPF自动反映UI中的更改。

  • 编辑项目属性:我们可能需要创建一个版本面板,我们放置TextBox和其他控件,以便我们修改每个设备的属性。

  • 添加支持多种类型的对象:为了使我们的应用程序完整,需要不同的对象类型和各自的视觉表示。



有关如何实现这些功能的示例,请参阅我的线编辑器 节点编辑器 样本。



我认为这对你的应用来说是一个很好的起点,也是对WPF的一个很好的介绍。
重要的是,您需要一些时间阅读所有链接的材料,才能更好地了解我们在此使用的概念和底层的API和功能。
如果您需要进一步的帮助或发布新问题,如果您需要实施任何额外的功能,请通知我。


我想我甚至不需要提到这样一个事实:在winforms中实现所有这些将会变得更加乏味,后面有很多代码,更多的工作和更差的结果。



I've been tasked with converting a VB6 Floor Plans application to VB.Net

The application creates Floor Plans using hundreds of dynamically created Images or Shapes placed and manipulated on the Form with X and Y coordinates (.Top, .Left).

For example: a poco data class looks something like this

public class Appliance {        
    public int Id      { get; set; }
    public string Name { get; set; }
    public int Top     { get; set; }
    public int Left    { get; set; }
    public int Width   { get; set; }
    public int Height  { get; set; }
    public int Type    { get; set; } 
    public int Color   { get; set; }
    public bool Visible{ get; set; }        
}

I started working on a FloorPlan class that contains lists of POCO objects like that which will represent the images or shapes and their positions on the form. After sketching out the following, I realized I must be doing it all wrong.

// Populate DATA objects from DBFiles
List<Appliance>  appliancesData = GetAppliancesFromDataFile()
List<PictureBox> appliancesUI   = new List<PictureBox>();

// create a bunch of PictureBox controls
foreach (var appliance in appliances){
  Image img = GetApplianceImage(appliance);
  Appliances.Add(new PictureBox { .Image = img })
  appliancesUI.Controls.Add(img);
}

// Add those PictureBox controls to the Form (via Panel)
foreach (var pic in appliancesUI){
  FormPanel.Controls.Add(pic);
}

I know there HAS to be a better way to do this. I need a link between the Raw Data in my classes to actual Image Controls added to the Form. There may not be a way to have 2way data-binding, but theres gotta be something better than this without deriving the poco classes from PictureBox controls.

What's the best way to sync the data between my data in my poco classes and the properties of the Form Image objects that will be created and added to the form and stop this madness?

解决方案

Since you're new to WPF, I'm going to go step by step on this, therefore it can get a little long and sometimes repeating, but bear with me.

Note: First of all, WPF might look like a complex, intimidating framework when you start looking into it, but it's not. It's actually really simple once you get to the realization that UI is not Data and start thinking all UI functionality in terms of DataBinding-based interactions between your UI components and your Data Model.

There's a very good series of articles by Reed Copsey, Jr. about the mindshift needed when moving from winforms to WPF. There's also a much shorter article by Rachel Lim I always point people to that gives a nice overview of the WPF mentality.

Step 1:

So, Let's use your Appliance class as a starting point for this:

public class Appliance 
{        
    public int Id         { get; set; }
    public string Name    { get; set; }
    public double Top     { get; set; }
    public double Left    { get; set; }
    public double Width   { get; set; }
    public double Height  { get; set; }
    public int Type       { get; set; } 
    public string Color   { get; set; }
    public bool Visible   { get; set; }        
}

Notice that I used double instead of int for size and position, because WPF actually uses doubles since those are device-independent units rather than pixels, and string for Color because it simplifies the example (we can actually use "Red", "Green", and so on).

Step 2:

So, the very first thing we need here in terms of UI, is a piece of UI that will "understand" it needs to take a List or Collection of our Appliance class and put a UI element on the screen for each item in the collection. Fortunately WPF provides that right out of the box, via the ItemsControl class.

Assuming we just created our project in Visual Studio using File -> New Project -> WPF Application, this is the default XAML for MainWindow:

<Window x:Class="FloorPlan.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
   <Grid></Grid>
</Window>

We'll get rid of the <Grid></Grid> part since we don't need that, and replace it by our ItemsControl:

<ItemsControl ItemsSource="{Binding}"/>

Notice that I'm Binding the ItemsSource property. WPF is going to resolve this simple Binding to whatever the DataContext of the ItemsControl is, therefore we will assign this in code behind (by now):

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        //Let's assign our DataContext here:
        this.DataContext = new List<Appliance>
        {
            new Appliance() {Top = 20, Left = 40, Width = 30, Height = 30, Color = "Red"},
            new Appliance() {Top = 100, Left = 20, Width = 80, Height = 20, Color = "Blue"},
            new Appliance() {Top = 60, Left = 40, Width = 50, Height = 30, Color = "Green"}
        };
    }
}

Notice that we're actually setting the Window's DataContext, rather than the ItemsControl, but thanks to WPF's Property Value Inheritance, the value of this property (and many others) is propagated down the Visual Tree.

Let's run our project to see what we have so far:

Not much is it? Don't worry, we're just getting started. The important thing to note here is that there's 3 "things" in our UI, which correspond to the 3 items in the List<Appliance> in our DataContext.

Step 3:

By default, the ItemsControl will stack elements vertically, which isn't what we want. Instead, we're going to change the ItemsPanel from the default StackPanel to a Canvas, like this:

<ItemsControl ItemsSource="{Binding}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>       
</ItemsControl>

And then, in order to have each UI element properly positioned and sized, we're going to Style the Item Containers so that they will take the values from the Top, Left, Width, and Height properties from the Appliance class:

<ItemsControl ItemsSource="{Binding}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>       

    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding Left}"/>
            <Setter Property="Canvas.Top" Value="{Binding Top}"/>
            <Setter Property="Width" Value="{Binding Width}"/>
            <Setter Property="Height" Value="{Binding Height}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>
</ItemsControl>

Notice that we're binding Canvas.Left and Canvas.Top as opposed to just Left and Top, because the WPF UI elements don't have a Top and Left properties themselves, but rather these are Attached Properties defined in the Canvas class.

Our project now looks like this:

Step 4:

Now we got sizing and positioning right, but we still get this ugly text instead of a proper visual for our Appliances. This is where Data Templating comes into play:

<ItemsControl ItemsSource="{Binding}">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>

    <ItemsControl.ItemContainerStyle>
        <Style TargetType="ContentPresenter">
            <Setter Property="Canvas.Left" Value="{Binding Left}"/>
            <Setter Property="Canvas.Top" Value="{Binding Top}"/>
            <Setter Property="Width" Value="{Binding Width}"/>
            <Setter Property="Height" Value="{Binding Height}"/>
        </Style>
    </ItemsControl.ItemContainerStyle>

    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border Background="{Binding Color}"/>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

By setting the ItemsControl.ItemTemplate we define how we want each item in the List<Appliance> to be visually represented.

Notice that I'm binding the Border.Background property to the Color property, which is defined in the Appliance class. This is possible because WPF sets the DataContext of each visual item in the ItemsControl to it's corresponding Data Item in the List<Appliance>.

This is our resulting UI:

Starting to look better, right? Notice we didn't even write a single line of code so far, this has all been done with just 20 lines of declarative XAML and DataBinding.

Next Steps:

So, now we have the basics in place, and we have achieved the goal of keeping the Appliance class completely decoupled from the UI, but still there are a lot of features that we may want to include in our app:

  • Selecting items by clicking on them: this can be achieved by changing the ItemsControl to a ListBox (while leaving its properties intact) just by using Ctrl+H. Since ListBox actually derives from ItemsControl, we can use the XAML we wrote so far.
  • Enable click-and-drag: This can be achieved by putting a Thumb control inside the DataTemplate and handling its DragDelta event.
  • Enable two-way DataBinding: This will allow you to modify the properties in the Appliance class and have WPF automatically reflect the changes in the UI.
  • Editing items' properties: We might want to create an edition panel where we put TextBoxes and other controls that will allow us to modify the properties of each appliance.
  • Add support for multiple types of objects: For our app to be complete, it will need different object types and their respective visual representations.

For examples of how to implement these features, see my Lines Editor and Nodes Editor samples.

I think this will be a good starting point for your app, and a good introduction to WPF as well. It's important that you take some time to read all the linked material to get a solid understanding of the concepts and underlying APIs and features that we're using here. Let me know if you need further help or post a new question if you need to implement any of the extra features.

I think I don't even need to mention the fact that implementing all this in winforms would be significantly more tedious, with lots of code behind and much more work and an inferior result.

这篇关于.Net WinForms设计同步数据和控件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆