chincheta73
chincheta73

Reputation: 217

WPF: Adding resource element to canvas and binding properties

I have programmed an application that draws elements in a canvas. These elements are Rectangles (but I used the Border class because I want to instert text in it). These elements represent objects (Tasks in my case).

I draw these elements in code behind like this:

foreach (var task in TasksList)
{
    var rect = new Border();
    rect.Background = (SolidColorBrush)(new 
        BrushConverter().ConvertFrom("#0074D9"));
    rect.BorderBrush = (SolidColorBrush)(new 
        BrushConverter().ConvertFrom("#001f3f"));
    rect.BorderThickness = new Thickness(2);

    rect.Width = 60;
    rect.Height = 60;
    var t = new TextBlock
    {
        Text = task.Id.ToString(),
        Foreground = new SolidColorBrush(Colors.White),
        HorizontalAlignment = HorizontalAlignment.Center,
        VerticalAlignment = VerticalAlignment.Center
    };
    rect.Child = t; 

    Canvas.SetLeft(rect, coordX);
    Canvas.SetTop(rect, coordY);
    Canvas.Children.Add(rect);
}

Insted of designing the border (rect) in code behind. I would like to design it as a XAML resource and create instances of this resources in code behind. How can accomplish this? How can I use bindings in this case? For instance, in the xaml resource I need to define that the property ID of the task needs to be binded to the Text property of a TextBlock placed in the middle of the border. But later, in the code behind, how do I specify the DataContext of the properties defined in the xaml?

Hope you could guide me. Thanks

Upvotes: 0

Views: 1338

Answers (1)

grek40
grek40

Reputation: 13458

It's not really clear where the coordinate values come from, so I turned them into part of the TaskItem class. You can change this aspect however you may want.

In order to realize your code in WPF XAML, you need a visual representation of the item (DataTemplate) and a way to determine the item placement - I use a style for this aspect. The items will be placed on the canvas with an ItemsControl as suggested by Clemens.

The TaskItem is a plain class for this example. If you want to modify its contents after creation, you should better implement it with INotifyPropertyChanged.

public class TaskItem
{
    public int Id { get; set; }
    public double CoordX { get; set; }
    public double CoordY { get; set; }
}

The xaml, expecting the DataContext to contain a TasksList collection:

<Window.Resources>
    <DataTemplate x:Key="dtTaskItem" DataType="{x:Type local:TaskItem}">
        <Border
            Background="#0074D9"
            BorderBrush="#001F3F"
            BorderThickness="2"
            Width="60"
            Height="60">
            <TextBlock
                Text="{Binding Id}"
                Foreground="White"
                HorizontalAlignment="Center" VerticalAlignment="Center"/>
        </Border>
    </DataTemplate>

    <Style x:Key="canvasTaskItemStyle" TargetType="ContentPresenter">
        <Setter Property="Canvas.Left" Value="{Binding CoordX}"/>
        <Setter Property="Canvas.Top" Value="{Binding CoordY}"/>
    </Style>
</Window.Resources>
<Grid x:Name="grid1">
    <ItemsControl
        ItemsSource="{Binding TasksList}"
        ItemTemplate="{StaticResource dtTaskItem}"
        ItemContainerStyle="{StaticResource canvasTaskItemStyle}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>
    </ItemsControl>
</Grid>

And some example data to play with:

var tasksList = new List<TaskItem>()
{
    new TaskItem { Id = 1, CoordX = 10, CoordY = 20 },
    new TaskItem { Id = 2, CoordX = 60, CoordY = 160 },
    new TaskItem { Id = 5, CoordX = 140, CoordY = 80 },
    new TaskItem { Id = 3, CoordX = 50, CoordY = 50 },
    new TaskItem { Id = 8, CoordX = 100, CoordY = 100 },
};
grid1.DataContext = new { TasksList = tasksList };

Sidenote: the resources could as well be placed inside a Grid.Resources section. The Window.Resources is just a default build I use when testing my answers to some WPF related stackoverflow question. The local:TaskItem expects the xaml namespace to be defined for the current project (In my case xmlns:local="clr-namespace:WpfTests_2").

Upvotes: 1

Related Questions