tags:

views:

123

answers:

2

In GDI+/WinForms, I could do this in the Click() event and using the graphics Object:

AddPoint(p); //Add Point contains some code to make sure there is only 3 dots
foreach (Point p in PointList) {
    DrawRectangle(p);
}
Invalidate();

If I try something similar in WPF, it won't cleanup the dots I created (I'm guessing because of how WPF works). What this means is if I check to make sure there is only three dots at a time, and pop off the oldest point to make room for the new one, the rectangle drawn would be still there.

So the question is, how can I create something in WPF that allows me to

  • Draw a rectangle at a Point
  • Remove rectangles/points from a WPF canvas after there is more than 3
+1  A: 

I would use wpf data binding to bind the content of the canvas to a collection of rectangles you store elsewere. You need to learn databinding anyway if you want to do serious WPF development.

Edit: Of course you store just the Rectangles, the data binding should create a shape for each rectangle.

jdv
How do I databind rectangles with positions (x/y)?
DMan
This is the geometry primitive you need: http://msdn.microsoft.com/en-us/library/system.windows.media.rectanglegeometry.aspx
jdv
And check this answer for a similar question: http://stackoverflow.com/questions/889825/wpf-is-it-possible-to-bind-a-canvass-children-property-in-xaml/1030191#1030191
jdv
+2  A: 

You're doing WPF the WinForms way. Don't do that. It's like writing VB code in C++. It can only end in tears.

To do this the WPF way, use databinding and a view model class to do the logic of "no more than 3 at a time." Then, for the UI, just bind to the PointList in your view model.

Here's what my XAML should look like. Notice I'm just using an ItemsControl and a Canvas, then binding the ItemsSource to PointList:

<Window x:Class="WpfTester.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">

    <ItemsControl ItemsSource="{Binding Path=PointList}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas />
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
        <ItemsControl.ItemTemplate>
            <DataTemplate>
                <Rectangle Fill="Red" Width="25" Height="25" />
            </DataTemplate>
        </ItemsControl.ItemTemplate>
        <ItemsControl.ItemContainerStyle>
            <Style>
                <Setter Property="Canvas.Left" Value="{Binding Path=X}" />
                <Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Window>

Then we just need to create the PointList. We'll use the normal WPF means: a view model class to hold the point list:

class MainViewModel
{
    public MainViewModel()
    {
        PointList = new ObservableCollection<Point>();

        // Some example data:
        AddPoint(new Point(10, 10));
        AddPoint(new Point(200, 200));
        AddPoint(new Point(500, 500));
    }

    public ObservableCollection<Point> PointList { get; private set; }

    public void AddPoint(Point p)
    {
        // 3 at most, please!
        if (PointList.Count == 3)
        {
            PointList.RemoveAt(0);
        }
        PointList.Add(p);
    }
}

Piece of cheese, yeah? So the final part is just telling the XAML to load your view model. Inside the the code-behind for your XAML, set the DataContext to your view model:

// Inside MainWindow.xaml.cs
public MainWindow()
{
    InitializeComponent();

    // Add this line:
    this.DataContext = new MainViewModel();
}

Now that you've got that in place, you can add/remove rectangles anywhere in your code simply by calling viewModel.AddPoint or viewModel.PointList.Remove, and the UI will automatically update to reflect the changes.

Judah Himango
I think I explained it very clearly at the bottom of my question: 1) Draw a rectangle at a Point2) Remove rectangles/points from a WPF canvas after there is more than 3.And yes, I sadly know I'm doing it the WinForms way :(
DMan
Ok. I'll update my answer to include a proper way to do this.
Judah Himango
Ok, I've updated my answer to include an example of a proper way to do this with WPF databinding.
Judah Himango
Thanks, I'm reading it right now.
DMan
Okay, I wrapped the ItemsControl in a Canvas then hooked the MouseDown() event on that- in that method, I created the new viewModel and called the AddPoint. However, I don't see it adding any rectangles.
DMan
Create the view model once, in the constructor. Call AddPoint in your MouseDown handler. Also, you shouldn't be wrapping an ItemsControl in a Canvas, but rather, using an ItemsControl whose ItemsPanelTemplate is a Canvas.
Judah Himango
Okay, I got it working. I did this:`MainViewModel mvm= new MainViewModel(); this.DataContext= mvm; mvm.AddPoint(new Point(x,y));`. Worked like a charm!EDIT: Just saw your comment, exactly what I was thinking :)
DMan
Yep. Now if you want to add points in the MouseDown handler, just call mvm.AddPoint inside your MouseDown handler, and they'll show up automagically.
Judah Himango
Thanks, great! Just more info: I hooked MouseDown() to the ItemsControl, and set ItemsControl's background to white/invisible so it would catch the events. One more question- do you have any idea how to make the point appear in the middle of the cursor, not 0,0?
DMan
If all your rectangles are the same size, (e.g. 25 in this example), just do something like AddPoint(MousePositionX - (25 / 2), MousePositionY - (25 / 2))
Judah Himango