Reputation: 217
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
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