I would like to add some Checkbox in a Grid control dynamically when the window is loaded in my C# Desktop application. How many times the checkbox will appear depends on the number of entries in a table. Here, I used LINQ To SQL class.
The Grid control is defined in XAML.
...
<Grid Name="grid1">
<!-- here i would like to show all check box -->
</Grid>
...
Code behind file:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
// class declaration ...
...
private void course_Loaded(object sender, RoutedEventArgs e)
{
List<Course> courses = ldc.Courses.ToList();
foreach (var c in courses)
{
CheckBox cb = new CheckBox();
cb.Name=c.CourseID.ToString();
cb.Content = c.CourseID.ToString();
//grid1.Controls.Add(cb); does not work. what to do here?
}
}
This code is not working. Any suggession?
Thankyou.
I suggest adding these CheckBoxes to a StackPanel and then add the StackPanel to the grid:
StackPanel innerStack;
private void course_Loaded(object sender, RoutedEventArgs e)
{
innerStack= new StackPanel
{
Orientation=Orientation.Vertical
};
List<Course> courses = ldc.Courses.ToList();
foreach (var c in courses)
{
CheckBox cb = new CheckBox();
cb.Name = c.CourseID.ToString();
cb.Content = c.CourseID.ToString();
innerStack.Children.Add(cb);
}
Grid.SetColumn(innerStack, /*Set the column of your stackPanel, default is 0*/);
Grid.SetRow(innerStack, /*Set the row of your stackPanel, default is 0*/);
Grid.SetColumnSpan(innerStack, /*Set the columnSpan of your stackPanel, default is 1*/);
Grid.SetRowSpan(innerStack, /*Set the rowSpan of your stackPanel, default is 1*/);
Grid.Children.Add(innerStack);
}
If you do not want this structure, you should add some RowDefinition to your grid and use Grid.SetRow(cb, int) method to put ComboBoxes over each other.
You're doing it wrong.
First to say you can do grid1.Children.Add(cb);
Then the real issue is that you're using a grid to display a list. There's a very nice ListView for that in WPF with completely style-able rows that can include checkboxes and pretty much everything else you can imagine.
I don't know what your data looks like so I couldn't expand much on the ListView but something like
<ListView ItemsSource="{Binding Courses}">
<ListView.View>
<GridView>
<GridViewColumn Width="120">
<GridViewColumnHeader>
<TextBlock Text="Course Name"/>
</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding ...UFigureThisOut}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="120">
<GridViewColumnHeader>
<TextBlock Text="Take That"/>
</GridViewColumnHeader>
<GridViewColumn.CellTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding ...UFigureThisOutToo}"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
Related
I cannot find a similar question or answer to what I am asking but if there is one out there I do apologize. What I am looking to achieve is to simply loop through all my controls in a user control and then write to a string builder in the order of the loop.
The code below outlines a simple example and I did try some limited things based on me being knew to C# etc.
<Window x:Class="CopyandPaste.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:CopyandPaste"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid x:Name="MainGrid">
<Button x:Name="CopyButton" Content="Copy Button" HorizontalAlignment="Left" Margin="10,34,0,0" VerticalAlignment="Top" RenderTransformOrigin="-4.265,-0.098" Height="41" Width="120" Click="CopyButton_Click"/>
<Grid x:Name="MasterGrid" HorizontalAlignment="Left" Height="378" Margin="146,10,0,0" VerticalAlignment="Top" Width="572">
<TextBox HorizontalAlignment="Center" Margin="0,21,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="408" Height="23" Grid.Column="1"/>
<RadioButton x:Name="RadioButton" Content="RadioButton" HorizontalAlignment="Left" Margin="82,55,0,0" VerticalAlignment="Top" Height="15" Width="85"/>
<ComboBox x:Name="ComboBox" HorizontalAlignment="Left" Margin="82,95,0,0" VerticalAlignment="Top" Width="369" Height="22">
<ComboBoxItem>THIS IS IT 1</ComboBoxItem>
<ComboBoxItem>THIS IS IT 2</ComboBoxItem>
<ComboBoxItem>THIS IS IT 3</ComboBoxItem>
</ComboBox>
<TextBox HorizontalAlignment="Center" Margin="0,140,0,0" TextWrapping="Wrap" VerticalAlignment="Top" Width="408" Height="23"/>
<TextBox HorizontalAlignment="Center" TextWrapping="Wrap" VerticalAlignment="Center" Width="408" RenderTransformOrigin="0.333,0.782" Height="28"/>
</Grid>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace CopyandPaste
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void CopyButton_Click(object sender, RoutedEventArgs e)
{
bool first = true;
StringBuilder value = new StringBuilder();
// some loop here
Clipboard.SetText(value.ToString());
}
}
}
I played with some foreach loops that were nested but of course got some results that were unwanted:
1
RadioButton
THIS IS IT 2
2
RadioButton
THIS IS IT 2
3
RadioButton
THIS IS IT 2
I understand what is going on here where the text box is causing the loop to continue. Trying to get some ideas or a point in the right direction to achieve an output like:
1
RadioButton
THIS IS IT 2
2
3
Any WPF UIElement has a Children property which will give you all its child controls. So you can use it to enumerate children and then loop through them. Something like;
foreach (var child in MasterGrid.Children)
{
// string builder code
}
You haven't quite clearly explained what your criteria to display a control is, but from the limited explanation you've given it looks like you want to grab the Text property if it's a TextBox, the SelectedItem if it is a ComboBox, and the name of the control if it is a RadioButton. These are quite different things when it comes to a control, so inside the loop you will have to check the type of each child control and get the right information.
private void CopyButton_Click(object sender, RoutedEventArgs e)
{
StringBuilder value = new StringBuilder();
foreach (var child in MasterGrid.Children.OfType<Control>())
{
if (child.GetType() == typeof(TextBox))
{
value.Append(((TextBox)child).Text + Environment.NewLine);
}
else if (child.GetType() == typeof(RadioButton))
{
var rb = (RadioButton)child;
if (rb.IsChecked == true)
{
value.Append(rb.Name + Environment.NewLine);
}
}
else if (child.GetType() == typeof(ComboBox))
{
value.Append(((ComboBox)child).Text + Environment.NewLine);
}
}
MessageBox.Show(value.ToString());
}
Above is for the types of controls you've mentioned, you'll have to figure out what to do with other control types.
Next when it comes to order, there can be different meanings to the 'order' in controls in a WPF GUI.
Do you want the order in which they are listed in the XAML code?
Or do you want the order in which they are displayed when the program is run?
For example, in your code if you moved the last text box XAML code above the second-to-last, they will still appear in the original order when you run the program because you've hard-coded their locations (which in itself is a bad idea; you should use grids, stack panels etc to do your layout in WPF).
I think if you care about the order, best option is to modify your XAML and specify the Tag property of each control.
E.g.
<TextBox Grid.Column="1"
Width="408"
Height="23"
Margin="0,21,0,0"
HorizontalAlignment="Center"
VerticalAlignment="Top"
Tag="1"
TextWrapping="Wrap"/>
Then when you enumerate the children, do an order by tag before iterating through them.
This way you have firm control over whatever it is that you mean by order.
var children = MasterGrid.Children.OfType<Control>().OrderBy(x => x.Tag);
foreach (var child in children)
{
// Same as before
}
I need to create interactive menu. When option was choosen, I want to show appropriate content.
For example when option "Schemat bazy Northwind" was clicked, to my grid should be added Image. When another option was choosen previous content is removed and etc.
The only thing that comes to my mind is create functions which at the beginning clear grid and later add content(Is it possible?).
Please there anybody could direct me to solve this problem.
<Window x:Class="Northwind.AdminPanel"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Panel administratora" WindowState="Maximized">
<StackPanel Name="bindingData">
<StatusBar>
<TextBlock FontSize="15" Text="{Binding ServerName}" Margin="0 0 30 0"></TextBlock>
<TextBlock FontSize="15" Text="{Binding ConnectionStatus}" Margin="0 0 30 0"></TextBlock>
<Label FontSize="15" Name="lblClock"></Label>
</StatusBar>
<DockPanel Height="55">
<Menu DockPanel.Dock="Top">
<MenuItem Header="Baza" Margin="10" FontSize="15"></MenuItem>
<MenuItem Header="Pomoc" Margin="10" FontSize="15">
<MenuItem x:Name="itemSchema" Header="Schemat bazy Northwind" Click="itmSchema_Click_1"></MenuItem>
</MenuItem>
</Menu>
</DockPanel>
<Grid x:Name="mainContent">
<!--add content -->
</Grid>
</StackPanel>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.Windows.Threading;
namespace Northwind
{
public partial class AdminPanel : Window
{
public string ServerName { get; set; }
public string ConnectionStatus { get; set; }
public AdminPanel(string name,string status)
{
InitializeComponent();
this.ServerName = name;
this.ConnectionStatus = status;
DispatcherTimer dtClockTime = new DispatcherTimer();
dtClockTime.Interval = new TimeSpan(0, 0, 1);
dtClockTime.Tick += dtClockTime_Tick;
dtClockTime.Start();
bindingData.DataContext = this;
}
private void dtClockTime_Tick(object sender, EventArgs e)
{
lblClock.Content = DateTime.Now.ToLongTimeString();
}
private void itmSchema_Click_1(object sender, RoutedEventArgs e)
{
//code
}
}
}
You can put the menu or whichever item you wanted to a conatainer (Grid) and you can use the Visibility attribute as collapsed when you want to hide that control.
if you are not using MVVM pattern then add it into the corresponding Event.
Container_Name.Visibility = Visibility.Collapsed;
Or else you can use the Xaml Triggers for obtain the same.
For that refer StackOverflow_Answer
Create a resources Dictionary and add this code
<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Set names two different ways -->
<Button Name="okButton">OK</Button>
<Button x:Name="cancelButton">Cancel</Button>
<ListBox>
<!-- Set content three different ways -->
<ListBoxItem Content="Item 1" />
<ListBoxItem>Item 2</ListBoxItem>
<ListBoxItem>
<ListBoxItem.Content>Item 3</ListBoxItem.Content>
</ListBoxItem>
</ListBox> </StackPanel>
when option "Schemat bazy Northwind" was clicked
private void SchematbazyNorthwind_Click(object sender, RoutedEventArgs e)
{
StackPanel stackPanel = null;
using (FileStream fs =
new FileStream("Dictionary1.xaml", FileMode.Open, FileAccess.Read))
{
stackPanel = (StackPanel)XamlReader.Load(fs);
}
MainGrid.Children.Add(stackPanel);
}
Load your Resources and attach to you grid
I mean how should I display my datastructure for I can work with it like with table?
I want to have a table with rows and columns can be dynamicaly added and removed, but in the rest is should looks like a table.
Now it's represented like IList>, because I should resize it, as I said before. But now i want to display it in DataGrid, and be able to have rows, coulmns, and work with "Cells". But i cannot bind it properly, it it does not display anything, only an empty cell for every row.
What should I do? Or mby use arrays and resize them after each row/column addition?
Please, advice.
Now I have this one:
private void MainWindow_OnLoaded(object sender, RoutedEventArgs e)
{
var storage = new Storage();
storage.Data.Add(new DataRow("Bla-bla")
{
new DataEntity() {Text = "bla-bla", Forces = new[] {ForceEnum.AA}},
new DataEntity() {Text = "bla-bla", Forces = new[] {ForceEnum.UA}}
});
DataListView.DataContext = new StorageModel(storage);
}
public class StorageModel
{
public StorageModel()
{
}
public StorageModel(IStorage storage)
{
DataRowList = new ObservableCollection<DataRow>(storage.Data);
}
public ObservableCollection<DataRow> DataRowList
{
get;
set;
}
}
public class DataRow : IList<DataEntity>
{
public string Name { get; private set; }
private readonly List<DataEntity> _list = new List<DataEntity>();
...
_
<ListView ItemsSource="{Binding DataRowList}" Name="DataListView">
<ListView.ItemTemplate>
<DataTemplate>
<ListView ItemsSource="{Binding}">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
i want be able to create something similar to this, but with 2-way binding...
You seem to be asking for TreeView and not ListView since you have a tree alike data structure.
If you do not want to use a TreeView I would suggest you to use a simple ListView with a small trick. That is you could explose an Expander which will add or remove items underneath parent with an indent to fake tree look and still all cells in column would be resized together.
Btw dont forgot to define columns
Here is how:
<ListView Margin="10" Name="lvUsers">
<ListView.View>
<GridView>
<GridViewColumn Header="Name" Width="120" DisplayMemberBinding="{Binding Name}" />
<GridViewColumn Header="Age" Width="50" DisplayMemberBinding="{Binding Age}" />
<GridViewColumn Header="Mail" Width="150" DisplayMemberBinding="{Binding Mail}" />
</GridView>
</ListView.View>
</ListView>
If you wish to implement that expander trick behavior I would suggest you to listen to event OnExpanded and add or remove the needed rows.
I am trying to display data from my SQL Server Compact 3.5. On my OnNavigateTo function, I have stated the codes but I am not sure why it is not able to load it. I am using Pivot App, Is it possible to use that to display my data? If yes, what have I done wrong. In the header=today is where I am displaying my data. Thanks.
Below are my codes
MainPage.xaml
<!--Pivot Control-->
<phone:Pivot Title="DAILY ROUTINE">
<!--Pivot item one-->
<phone:PivotItem Header="activity">
<!--Double line list with text wrapping-->
<phone:LongListSelector x:Name="MLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="LongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding LineOne}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</phone:PivotItem>
<!--Pivot item two-->
<phone:PivotItem Header="today">
<!--Double line list with text wrapping-->
<phone:LongListSelector x:Name="MainLongListSelector" Margin="0,0,-12,0" ItemsSource="{Binding Items}" SelectionChanged="LongListSelector_SelectionChanged">
<phone:LongListSelector.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17">
<TextBlock Text="{Binding Id}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding Title}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
</StackPanel>
</DataTemplate>
</phone:LongListSelector.ItemTemplate>
</phone:LongListSelector>
</phone:PivotItem>
</phone:Pivot>
MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;
using MyPhoneApp1.Resources;
namespace MyPhoneApp1
{
public partial class MainPage : PhoneApplicationPage
{
// Constructor
public MainPage()
{
InitializeComponent();
// Set the data context of the listbox control to the sample data
DataContext = App.ViewModel;
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
// Load data for the ViewModel Items
protected override void OnNavigatedTo(NavigationEventArgs e)
{
/*
if (!App.ViewModel.IsDataLoaded)
{
App.ViewModel.LoadData();
}
* */
using (ToDoListContext c = new ToDoListContext(ToDoListContext.ConnectionString))
{
c.CreateIfNotExists();
c.LogDebug = true;
MainLongListSelector.ItemsSource = c.ToDoLists.ToList();
}
}
private void LongListSelector_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var si = MLongListSelector.SelectedItem as MyPhoneApp1.ViewModels.ItemViewModel;
if (MLongListSelector.SelectedItem == null)
return;
if (si.LineOne.Equals("+ To Do List"))
NavigationService.Navigate(new Uri("/todolistPage.xaml", UriKind.Relative));
else if (si.LineOne.Equals("+ Reminder"))
NavigationService.Navigate(new Uri("/reminderPage.xaml", UriKind.Relative));
// Reset selected item to null (no selection)
MLongListSelector.SelectedItem = null;
}
}
}
I have debugged it and below is the SQL Statements
SELECT [t0].[Id], [t0].[Title]
FROM [ToDoLists] AS [t0]
I wouldn't suggest you directly set the ItemsSource property as you've already established a Binding in the XAML:
<phone:LongListSelector x:Name="MLongListSelector"
ItemsSource="{Binding Items}" >
Since the Binding Path is set to Items, changing the data of the list stored in Items will cause the UI to update automatically.
// get the list ...
var list = c.ToDoLists.ToList();
Debug.Assert(list != null);
// clear any existing items, which will in turn remove all items from the UI
App.ViewModel.Items.Clear();
// for each item in the list, add it to the existing bound Items list
foreach(var item in list) {
// you may need to transform the data here
// The item must be the right type ...
App.ViewModel.Items.Add(item);
}
As it looks like you're using the WP8 template, the ToDoLists property needs to return an enumerable list of ItemViewModels or the call to Add will fail. You could create new instances of an ItemViewModel if the types don't match (for example):
var itemViewModel = new ItemViewModel() {
LineOne = item.Text,
LineTwo = item.Description
};
App.ViewModel.Items.Add(itemViewModel);
The above code assumes then that a todo list item might look like this:
public class TodoItem {
public string Text { get; set; }
public string Description { get; set; }
}
I think this might be the problem you are binding the MainLongListSelector twice
in Xaml
ItemsSource="{Binding Items}"
and in c#
MainLongListSelector.ItemsSource = c.ToDoLists.ToList();
Looks like you need to remove the xaml binding
This is a really basic requirement, but I'm stuck! For WPF/.Net - I just want to dynamically draw into a Canvas column in my ListView. One failed attempt:
<ListView name="myGridView">
<GridViewColumn Header="ColumnA" DisplayMemberBinding="{Binding Path=ColumnA}" />
<GridViewColumn DisplayMemberBinding="{Binding Path=ColumnB}">
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ SUSPECT!
<Canvas Name="canvasColumn" Width="100" Height="20" />
</GridViewColumn>
</GridView>
Inside my code, I have a class "MyData" with fields bound to the non-canvas ListView columns. I loop through some "Whatever"s creating items in the ListView:
foreach (Whatever whatever in whatevers)
{
MyData myData = new MyData();
myData.ColumnA = whatever.A;
myData.ColumnB = new Canvas();
Line line = new Line();
line.Stroke = System.Windows.Media.Brushes.Black;
line.X1 = line.Y1 = 1;
line.X2 = line.Y2 = 100;
line.StrokeThickness = 1;
myData.ColumnB.Children.Add(line);
myListView.Items.Add(myData);
}
This DOES NOT work: every row in the on-screen canvas column displays the text "System.Windows.Controls.Canvas". Not terribly surprising - I've bound the column in the same way as the text columns and some toString conversion of the typename seems to kick in. But, I've tried a few other things and just can't get the Canvas displayed.
I have also tried removing the column binding marked "SUSPECT" above, and myData's ColumnB field, seeking a way to refer to the canvas widgets via the listview, i.e. something of the form:
myListView.reference-to-new-row-and-canvas-column = theNewCanvasIDrewOn;
Some of my searches have turned up ugly messes of Styles, ItemPanel configs etc.: please - if that's necessary, I at least hope it can be kept minimal....
Any guidance greatly appreciated.
Cheers,
Tony
UPDATE
For my purposes, the minimal solution appears to be adding a DataTemplate to App.xaml's Application.Resources tag:
<DataTemplate x:Key="myTemplate">
<Canvas Width="60" Height="20" Background="Red" ClipToBounds="True" >
<ContentPresenter Content="{Binding myCanvasField}" />
</Canvas>
</DataTemplate>
and defining a GridViewColumn as:
<GridViewColumn CellTemplate="{StaticResource myTemplate}" Header="title" />
Thanks to Dean for pointing me in the right direction, and to Binding to Canvas for canvas-specific details. I then "draw on" the Canvas property member of the object I add to the ListView.
you need to use a CellTemplate rather than a Canvas directly
http://msdn.microsoft.com/en-us/library/system.windows.controls.gridviewcolumn.celltemplate.aspx
You could impliment the TaskVisualizer as a custom control and then just host that in your list template. This separates out your task visualization code from your global UI code. This has the advantage that its easy to reuse the task visualation else where - eg you could easily show the same graphic in a tooltip when hovering over a task in some other view.
here's my take on it. The idea is to use a mini DSL to exchange the information between your canvas and your business objects.
XAML:
<Window x:Class="DrawInCanvas.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DrawInCanvas"
Title="MainWindow" Height="350" Width="525">
<Grid>
<DataGrid x:Name="g">
<DataGrid.Columns>
<DataGridTextColumn Header="Id" Binding="{Binding Item1}" />
<DataGridTemplateColumn Header="Bar">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Canvas HorizontalAlignment="Left"
Height="20"
local:CanvasDrawing.Drawing="{Binding Item2}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace DrawInCanvas
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(MainWindow_Loaded);
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
// just a sample
Dictionary<int, string> barDefinitions = new Dictionary<int, string>(3)
{
{ 1, "100$red" },
{ 2, "220$yellow" },
{ 3, "40$blue" }
};
this.g.ItemsSource =
Enumerable.Range(1, 3).Select(t =>
new Tuple<int, string>(t, barDefinitions[t]));
}
}
public class CanvasDrawing : DependencyObject
{
public static readonly DependencyProperty DrawingProperty =
DependencyProperty.RegisterAttached("Drawing",
typeof(string),
typeof(CanvasDrawing),
new PropertyMetadata(new PropertyChangedCallback((o, e) =>
{
CanvasDrawing.Draw((Canvas)o, (string)e.NewValue);
})));
public static void SetDrawing(Canvas canvas, string drawing)
{
canvas.SetValue(CanvasDrawing.DrawingProperty, drawing);
}
public static string GetDrawing(Canvas canvas)
{
return (string)canvas.GetValue(CanvasDrawing.DrawingProperty);
}
private static void Draw(Canvas canvas, string drawing)
{
string[] parts = drawing.Split("$".ToCharArray());
canvas.Width = double.Parse(parts[0]);
canvas.Background = new SolidColorBrush((Color)ColorConverter.ConvertFromString(parts[1]));
}
}
}