I have to assign the class object to the cell of the WPF grid. The class object has one string variable_name. But when I assigned the observablecollection of that particular class, it assign the variable name throughout the particular datagrid row instead of cell. I know that the number of datagrid rows are equal to the number of items present in itemsource assigned collection, but I need to assign to single cell not row. There are many items that I need to assign to each cell but if variable issue is resolved I can modify other code as well.
Screenshot:
public partial class MainWindow : Window
{
public string exp = "A+B";
public class GraphObj: INotifyPropertyChanged
{
string _variable_name = "";
public string variable_name
{
get { return _variable_name; }
set {
_variable_name = value;
OnPropertyChanged("variable_name")
}
}
public GraphObj()
{
variable_name = "A";
}
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string prop)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(prop));
}
}
public MainWindow()
{
InitializeComponent();
Rectangle screen = Screen.PrimaryScreen.WorkingArea;
int w = (int)(Width >= screen.Width ? screen.Width : (screen.Width + Width) / 2);
int h = (int)(Height >= screen.Height ? screen.Height : (screen.Height + Height) / 2);
dgGraph.Width = w;
dgGraph.Height = h;
graphObjArray = new ObservableCollection<GraphObj>();
InitilizeDataGrid();
}
private void InitializeDataGrid()
{
for (int j = 0; j < 16; j++)
{
GraphObj obj = new GraphObj();
graphObjArray.Add(obj);
}
DataTemplate d = mygrid.Resources["myTemplate"] as DataTemplate;
for (int i = 0; i < 16; i++)
{
DataGridTemplateColumn tmp2 = new DataGridTemplateColumn();
tmp2.CellTemplate = d;
tmp2.Width = dgGraph.Width / 16;
dgGraph.Columns.Add(tmp2);
}
dgGraph.ItemsSource = graphObjArray;
}
}
XAML:
<Window x:Class="WPF_Grid_Demo.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"
Loaded="Window_Loaded" >
<Grid x:Name="mygrid">
<Grid.Resources>
<Style TargetType="DataGridCell">
<Setter Property="Background" Value="White"/>
<Setter Property="BorderBrush" Value="White" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="ScrollViewer.CanContentScroll" Value="False"/>
</Style>
<DataTemplate x:Key="myTemplate" x:Name="mycelltemplate">
<Grid ShowGridLines="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="2*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Grid Grid.Column="0">
<Rectangle Fill= "Blue"/>
</Grid>
<Grid Grid.Column="2">
<Rectangle Fill="Green"/>
</Grid>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid Grid.Row="0">
<TextBlock Grid.Row="0" HorizontalAlignment="Center" VerticalAlignment="Center"
FontWeight="Bold" Text="{Binding variable_name}" />
</Grid>
<Grid Grid.Row="1">
<Image Stretch="UniformToFill" Source="{Binding imgURL}"/>
</Grid>
</Grid>
</Grid>
</DataTemplate>
</Grid.Resources>
<DataGrid x:Name="dgGraph"
GridLinesVisibility="None"
IsReadOnly="True"
AutoGenerateColumns="False"
CanUserResizeRows="False"
CanUserResizeColumns="False"
ScrollViewer.CanContentScroll="False"
ScrollViewer.HorizontalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
>
</DataGrid>
</Grid>
</Window>
Suppose I have a list which contains some elements and texts:
public MainWindow()
{
InitializeComponent();
TextBlock t = new TextBlock();
t.Text = "Hello World!";
Ellipse e = new Ellipse();
e.Width = 100;
e.Height = 100;
e.Fill = new SolidColorBrush(Colors.Yellow);
Rectangle r = new Rectangle();
r.Width = 70;
r.Height = 40;
r.Fill = new SolidColorBrush(Colors.Blue);
List<UIElementItem> items = new List<UIElementItem>();
items.Add(new UIElementItem() { uiElement = t, Text = "This is a TextBlock: " });
items.Add(new UIElementItem() { uiElement = e, Text = "This is an Ellipse: " });
items.Add(new UIElementItem() { uiElement = r, Text = "This is a Rectangle: " });
lbListBox.ItemsSource = items;
}
}
public class UIElementItem
{
public UIElement uiElement { get; set; }
public string Text { get; set; }
}
This is the ListBox in XAML:
<ListBox Name="lbListBox" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,2" Orientation="Horizontal">
<TextBlock Text="{Binding Text}"/>
<??? = "{Binding uiElement}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
I want to make the ListBox show the texts and the elements.
The TextBlock Text="{Binding Text}" will show the text but how to show the element? How to bind?
You could use a ContentPresenter
<ContentPresenter Content="{Binding uiElement}" />
This would show the UI Elements. The complete ItemTemplate would look like
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,2" Orientation="Horizontal">
<TextBlock Text="{Binding Text}"/>
<ContentPresenter Content="{Binding uiElement}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
I currently trying to "port" some control from WindowsForms to WPF.
I Have this stylish led checkbox and try to achieve the same visual appearance in wpf. but I'm unable to get it done.
I've searched a lot butt cannot find a solution to my questions/problems.
This is how the winforms Control looks like
The colored Circle size depends on the size of the control.
The color is user definable. The color is used for the circle and the Text.
It's bright if it ich checked and dimmed / gray when it's unchecked.
The diark and highlight colors are calculated from the control color (lighter/darker).
All my tries to do the same in wpf pretty much failed up to now. :-(
I fist tried to do it with an usercontrol, but decided it would be easier to have it derived from checkbox with just an extra option to set the color.
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:test="clr-namespace:LedTest"
xmlns:uc="clr-namespace:WPFTest;assembly=LedControl"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="LedTest.MainWindow"
Title="MainWindow" Height="285" Width="566">
<Window.Resources>
<ResourceDictionary x:Key="ResDict2" Source="Dictionary2.xaml"/>
</Window.Resources>
<Grid Margin="0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" MinHeight="27" />
<RowDefinition Height="75"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="10*" />
<ColumnDefinition Width="179*"/>
</Grid.ColumnDefinitions>
<uc:LedControl x:Name="led1"
Color="ForestGreen" Text="Some Option"
Grid.Column="1" Grid.Row="1" Height="39" VerticalAlignment="Bottom" Margin="0,0,0,36"/>
<CheckBox Content="Some Option" Style="{DynamicResource TestStyle}" Margin="0,0,31,0" Grid.Column="1"/>
</Grid>
</Window>
This is my LedControl code:
<UserControl x:Class="LedControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="100" d:DesignWidth="300">
<UserControl.Resources>
</UserControl.Resources>
<StackPanel x:Name="gridBigLed" Orientation="Horizontal" >
<Border x:Name="border1"
BorderThickness="1"
Width="{Binding ActualHeight, ElementName=gridBigLed, Mode=OneWay}"
CornerRadius="{Binding ActualWidth, ElementName=gridBigLed, Mode=OneWay}"
HorizontalAlignment="Left">
<Border.Background>
<RadialGradientBrush GradientOrigin="0.2,0.2">
<GradientStop Color="#FFFFAAAA"/>
<GradientStop x:Name="backgroundColor" Color="Red" Offset="1.2"/>
</RadialGradientBrush>
</Border.Background>
<Border.BorderBrush>
<RadialGradientBrush>
<GradientStop x:Name="GradientColorLow" Color="#FF660000" Offset="0.383"/>
<GradientStop x:Name="GradientColorHigh" Color="#330000" Offset="0.5"/>
</RadialGradientBrush>
</Border.BorderBrush>
</Border>
<Label Content="{Binding Text}" x:Name="LEDText" Foreground="Red" HorizontalContentAlignment="Left" VerticalContentAlignment="Center"/>
</StackPanel>
</UserControl>
and the code behind:
public partial class LedControl : UserControl
{
#region Dependency properties
/// <summary>Dependency property to Get/Set the current IsActive (True/False)</summary>
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool?), typeof(LedControl),
new PropertyMetadata(null, new PropertyChangedCallback(LedControl.IsCheckedPropertyChanced)));
/// <summary>Dependency property to Get/Set Color when IsActive is true</summary>
public static readonly DependencyProperty ColorProperty =
DependencyProperty.Register("Color", typeof(Color), typeof(LedControl),
new PropertyMetadata(Colors.Green, new PropertyChangedCallback(LedControl.OnColorPropertyChanged)));
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(LedControl),
new PropertyMetadata("ButtonText", new PropertyChangedCallback(LedControl.OnTextPropertyChanged)));
#endregion
#region Properties
/// <summary>Gets/Sets Text Value</summary>
public string Text { get { return (string)GetValue(TextProperty); } set { SetValue(TextProperty, value); } }
/// <summary>Gets/Sets Value</summary>
public bool? IsChecked { get { return (bool?)GetValue(IsCheckedProperty); } set { SetValue(IsCheckedProperty, value); } }
/// <summary>Gets/Sets Color</summary>
public Color Color { get { return (Color)GetValue(ColorProperty); } set { SetValue(ColorProperty, value); } }
#endregion
#region Constructor
public LedControl()
{
InitializeComponent();
if (this.IsChecked == true)
{
this.LEDColor.Color = this.Color;
this.LEDText.Foreground = new SolidColorBrush(this.Color);
}
else if (this.IsChecked == false)
{
this.LEDColor.Color = Colors.Gray;
this.LEDText.Foreground = new SolidColorBrush(Colors.Gray);
}
}
#endregion
#region Callbacks
private static void IsCheckedPropertyChanced(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LedControl led = (LedControl)d;
if (led.IsChecked == true)
{
led.LEDColor.Color = led.Color;
led.LEDText.Foreground = new SolidColorBrush(led.Color);
}
else
{
led.LEDColor.Color = Colors.Gray; // TODO calculate dark/gray color
led.LEDText.Foreground = new SolidColorBrush(Colors.Gray);
}
}
private static void OnColorPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LedControl led = (LedControl)d;
led.Color = (Color)e.NewValue;
if (led.IsChecked == true)
{
led.LEDColor.Color = led.Color;
led.LEDText.Foreground = new SolidColorBrush( led.Color );
}
}
private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
LedControl led = (LedControl)d;
led.Text = (String)e.NewValue;
}
#endregion
}
The thing is that the control does not work. I set the Color to forrestGreen, but shows up red in designer and if I execute the program:
The text "Some Option" is not shown as well..
I haven't figured out how to have the gradient colors to be darker and lighter versions of the color I want.
The look of the led is also not as cool as in winforms,
but I have no clue to translate the code to wpf.
here is the part of the code that draws the led in win-Forms:
private void drawControl(Graphics g, bool on) {
// Is the bulb on or off
Color lightColor = (on) ? this.Color : Color.FromArgb(100, this.Color);
Color darkColor = (on) ? this.DarkColor : Color.Gray/*this.DarkDarkColor*/;
// Calculate the dimensions of the bulb
int width = this.Width - (this.Padding.Left + this.Padding.Right);
int height = this.Height - (this.Padding.Top + this.Padding.Bottom);
// Diameter is the lesser of width and height
int diameter = Math.Min(width, height);
// Subtract 1 pixel so ellipse doesn't get cut off
diameter = Math.Max(diameter - 1, 1);
SolidBrush br = new SolidBrush(BackColor);
g.FillRectangle(br, ClientRectangle);
// Draw the background ellipse
var rectangle = new Rectangle(this.Padding.Left, this.Padding.Top, diameter, diameter);
g.FillEllipse(new SolidBrush(darkColor), rectangle);
// Draw the glow gradient
var path = new GraphicsPath();
path.AddEllipse(rectangle);
var pathBrush = new PathGradientBrush(path);
pathBrush.CenterColor = lightColor;
pathBrush.SurroundColors = new Color[] { Color.FromArgb(0, lightColor) };
g.FillEllipse(pathBrush, rectangle);
// Draw the white reflection gradient
var offset = Convert.ToInt32(diameter * .15F);
var diameter1 = Convert.ToInt32(rectangle.Width * .8F);
var whiteRect = new Rectangle(rectangle.X - offset, rectangle.Y - offset, diameter1, diameter1);
var path1 = new GraphicsPath();
path1.AddEllipse(whiteRect);
var pathBrush1 = new PathGradientBrush(path);
pathBrush1.CenterColor = _reflectionColor;
pathBrush1.SurroundColors = _surroundColor;
g.FillEllipse(pathBrush1, whiteRect);
// Draw the border
g.SetClip(this.ClientRectangle);
if (this.On)
g.DrawEllipse(new Pen(Color.FromArgb(85, Color.Black),1F), rectangle);
if (this.Text != string.Empty)
{
RectangleF textArea = this.ClientRectangle;
textArea.X += rectangle.Width + 6;
textArea.Width -= (diameter + 6);
Font fon = new Font(Font.FontFamily, Font.Size-1, FontStyle.Bold);
StringFormat sf = new StringFormat();
sf.Alignment = StringAlignment.Near;
sf.LineAlignment = StringAlignment.Center;
if (!this.On)
g.DrawString(this.Text, fon, new SolidBrush(Color.Gray), textArea, sf);
else
g.DrawString(this.Text, fon, new SolidBrush(darkColor), textArea, sf);
}
}
My second try with the checkbox as base is nore or less useless, but perhaps someone is keen and can substitute the checkbox with the led.
Any help is appreciated!
here is a LedControl derived from CheckBox. LedControl itself adds OnColor and OffColor properties.
public class LedControl : CheckBox
{
static LedControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(LedControl), new FrameworkPropertyMetadata(typeof(LedControl)));
}
public static readonly DependencyProperty OnColorProperty =
DependencyProperty.Register("OnColor", typeof(Brush), typeof(LedControl), new PropertyMetadata(Brushes.Green));
public Brush OnColor
{
get { return (Brush)GetValue(OnColorProperty); }
set { SetValue(OnColorProperty, value); }
}
public static readonly DependencyProperty OffColorProperty =
DependencyProperty.Register("OffColor", typeof(Brush), typeof(LedControl), new PropertyMetadata(Brushes.Red));
public Brush OffColor
{
get { return (Brush)GetValue(OffColorProperty); }
set { SetValue(OffColorProperty, value); }
}
}
and visual appearance is customized via Style and Template. Main template parts are LedBorder ellipse, white CenterGlow ellipse, white CornerLight shape and of course ContentPresent. LedBorder adapts to LedControl height. Depending on IsChecked LedBorder is colored with OnColor or OffColor (as well as Foreground). Disabled control is grayed.
<Style TargetType="local:LedControl">
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="BorderBrush" Value="Black"/>
<Setter Property="Margin" Value="5"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:LedControl">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid Background="Transparent" Name="grd"
Margin="{TemplateBinding Padding}"
VerticalAlignment="Stretch"
Width="{Binding Path=ActualHeight, Mode=OneWay, RelativeSource={RelativeSource Self}}">
<Ellipse x:Name="LedBorder"
Fill="{TemplateBinding Background}"
Stroke="{TemplateBinding BorderBrush}"
StrokeThickness="2"
Stretch="Uniform"/>
<Ellipse x:Name="CenterGlow" Stretch="Uniform">
<Ellipse.Fill>
<RadialGradientBrush>
<GradientStop Color="White" Offset="-0.25"/>
<GradientStop Color="Transparent" Offset="0.91"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
<Ellipse x:Name="CornerLight" Stretch="Uniform" Margin="2">
<Ellipse.Fill>
<RadialGradientBrush Center="0.15 0.15" RadiusX="0.5" RadiusY="0.5">
<GradientStop Color="White" Offset="0"/>
<GradientStop Color="Transparent" Offset="1"/>
</RadialGradientBrush>
</Ellipse.Fill>
</Ellipse>
</Grid>
<ContentPresenter x:Name="content" Grid.Column="1" Margin="4,0,0,0"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter TargetName="LedBorder" Property="Fill" Value="{Binding Path=OnColor, RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter TargetName="content" Property="TextElement.Foreground" Value="{Binding Path=OnColor, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter TargetName="LedBorder" Property="Fill" Value="{Binding Path=OffColor, RelativeSource={RelativeSource TemplatedParent}}"/>
<Setter TargetName="content" Property="TextElement.Foreground" Value="{Binding Path=OffColor, RelativeSource={RelativeSource TemplatedParent}}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter TargetName="CenterGlow" Property="Fill">
<Setter.Value>
<RadialGradientBrush Opacity="1">
<GradientStop Color="Transparent" Offset="-0.5" />
<GradientStop Color="#888" Offset="1" />
</RadialGradientBrush>
</Setter.Value>
</Setter>
<Setter TargetName="content" Property="TextElement.Foreground" Value="#888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and here is a sample:
<StackPanel>
<local:LedControl Content="Disabled OFF" Height="24" IsChecked="False" IsEnabled="False" />
<local:LedControl Content="Disabled ON" Height="32" IsChecked="True" IsEnabled="False" />
<local:LedControl Content="Enabled OFF" OffColor="Chocolate" IsChecked="False" Height="40" />
<local:LedControl Content="Enabled ON" OnColor="Navy" IsChecked="True" Height="48" />
</StackPanel>
You can use PathGradientBrush to draw a radial gradient. Here is the result of the code which I wrote. You can use any color as CheckedColor and UnCheckedColor, I used Red and Green to get this result:
Code
using System;
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;
public class MyCheckBox : CheckBox
{
public MyCheckBox()
{
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.DoubleBuffered = true;
this.ResizeRedraw = true;
CheckedColor = Color.Green; ;
UnCheckedColor = Color.Red; ;
}
[DefaultValue(typeof(Color), "Green")]
public Color CheckedColor { get; set; }
[DefaultValue(typeof(Color), "Red")]
public Color UnCheckedColor { get; set; }
protected override void OnPaint(PaintEventArgs e)
{
var darkColor = Color.Black;
var lightColor = Color.FromArgb(200, Color.White);
var cornerAlpha = 80;
this.OnPaintBackground(e);
using (var path = new GraphicsPath())
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
var rect = new Rectangle(0, 0, Height, Height);
path.AddEllipse(rect);
rect.Inflate(-1, -1);
using (var bgBrush = new SolidBrush(darkColor))
{
e.Graphics.FillEllipse(bgBrush, rect);
}
using (var pathGrBrush = new PathGradientBrush(path))
{
var color = Checked ? CheckedColor : UnCheckedColor;
pathGrBrush.CenterColor = color; ;
Color[] colors = { Color.FromArgb(cornerAlpha, color) };
pathGrBrush.SurroundColors = colors;
e.Graphics.FillEllipse(pathGrBrush, rect);
}
using (var pathGrBrush = new PathGradientBrush(path))
{
pathGrBrush.CenterColor = lightColor; ;
Color[] colors = { Color.Transparent };
pathGrBrush.SurroundColors = colors;
var r = (float)(Math.Sqrt(2) * Height / 2);
var x = r / 8;
e.Graphics.FillEllipse(pathGrBrush, new RectangleF(-x, -x, r, r));
e.Graphics.ResetClip();
}
}
TextRenderer.DrawText(e.Graphics, Text, Font,
new Rectangle(Height, 0, Width - Height, Height), ForeColor,
TextFormatFlags.Left | TextFormatFlags.VerticalCenter);
}
}
I'm new here so please excuse my if I missed to add something needed to answer my question.
So heres my question:
I am trying to add shapes to a canvas while also wanting to show a list of them in a listbox, to make them changeable (size,position, etc.). I am using WPF. Is there a way to do so?
And if it doesnt bother you: Is there maybe a question or website or whatever about how to dynamically draw shapes(circle,ellipse,rect, etc.) with mouse events?
I hope you can help me. Thanks in advance.
Edit:
Given the fact that I have:
public partial class MainWindow : Window
{
public ObservableCollection<string> Baselist = new ObservableCollection<string>();
public ObservableCollection<string> Crystallist = new ObservableCollection<string>();
public ObservableCollection<Shape> Shapelist = new ObservableCollection<Shape>();
public MainWindow()
{
this.ResizeMode = System.Windows.ResizeMode.CanMinimize;
InitializeComponent();
InitializeLists(Baseforms,CrystalGroups);
}
private void InitializeLists(ComboBox Baseforms, ComboBox CrystalGroups)
{
Baseforms.ItemsSource = Baselist;
CrystalGroups.ItemsSource = Crystallist;
Shape Circle = new Ellipse();
Circle.Stroke = System.Windows.Media.Brushes.Black;
Circle.Fill = System.Windows.Media.Brushes.DarkBlue;
Circle.HorizontalAlignment = HorizontalAlignment.Left;
Circle.VerticalAlignment = VerticalAlignment.Center;
Circle.Width = 50;
Circle.Height = 50;
Shapelist.Add(Circle);
}
How can I use an ItemsControl to show the shapes in Shapelist in an canvas while also listing them in a Listbox?
Hope this makes the question less broad.
Please try the next solution:
Updated version (xaml and code behind)
DetailsList list view - presents detailed data based on ShapeDataPresentation class (supports multi-select). Shows data by data template named ShapeDataPresentationDataTemplate.
ShapesPresentor items control - presents shapes on canvas (doesn't support multi-select only one can be selected).
ListView XAML code ("This" is the name of the ListView containing window)
<Window x:Class="ListViewWithCanvasPanel.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:listViewWithCanvasPanel="clr-namespace:ListViewWithCanvasPanel"
Title="MainWindow" Height="350" Width="525" x:Name="This" ResizeMode="CanResize">
<Grid>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2.5*"></ColumnDefinition>
<ColumnDefinition Width="4*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<ListView x:Name="DetailsList" Panel.ZIndex="999" Grid.Column="0" ItemsSource="{Binding ElementName=This, Path=Shapes}" SelectionMode="Extended" SelectionChanged="Selector_OnSelectionChanged">
<ListView.Resources>
<DataTemplate x:Key="ShapeDataPresentationDataTemplate" DataType="listViewWithCanvasPanel:ShapeDataPresentation">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding Name, StringFormat={}{0:N0}%}"></TextBlock>
<TextBlock Grid.Column="1">
<Run Text="W:"></Run>
<Run Text="{Binding OriginalRectAroundShape.Width}"></Run>
</TextBlock>
<TextBlock Grid.Column="2">
<Run Text="H:"></Run>
<Run Text="{Binding OriginalRectAroundShape.Height}"></Run>
</TextBlock>
</Grid>
</DataTemplate>
</ListView.Resources>
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate DataType="Shape">
<ContentControl Content="{Binding Tag}" ContentTemplate="{StaticResource ShapeDataPresentationDataTemplate}"></ContentControl>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListView.ItemContainerStyle>
</ListView>
<GridSplitter Grid.Column="0" Width="3" Background="Blue" Panel.ZIndex="999"
VerticalAlignment="Stretch" HorizontalAlignment="Right" Margin="0"/>
<ItemsControl Grid.Column="1" x:Name="ShapesPresentor" ItemsSource="{Binding ElementName=This, Path=Shapes}"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
MouseDown="UIElement_OnMouseDown">
<!--<ListView.Resources>
<ControlTemplate x:Key="SelectedTemplate" TargetType="ListViewItem">
<ContentControl Content="{Binding }"></ContentControl>
</ControlTemplate>
</ListView.Resources>-->
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="White" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<!--<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="true" />
<Condition Property="Selector.IsSelectionActive" Value="true" />
</MultiTrigger.Conditions>
<Setter Property="Template" Value="{StaticResource SelectedTemplate}" />
</MultiTrigger>
</Style.Triggers>
</Style>
</ListView.ItemContainerStyle>-->
</ItemsControl>
</Grid>
<StackPanel VerticalAlignment="Bottom" HorizontalAlignment="Stretch" Orientation="Horizontal">
<ComboBox x:Name="Baseforms"></ComboBox>
<ComboBox x:Name="CrystalGroups"></ComboBox>
</StackPanel>
</Grid>
Code behind (updated)
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public static readonly DependencyProperty ShapesProperty = DependencyProperty.Register(
"Shapes", typeof (ObservableCollection<Shape>), typeof (MainWindow),
new PropertyMetadata(default(ObservableCollection<Shape>)));
public ObservableCollection<Shape> Shapes
{
get { return (ObservableCollection<Shape>) GetValue(ShapesProperty); }
set { SetValue(ShapesProperty, value); }
}
public ObservableCollection<string> Baselist = new ObservableCollection<string> {"a", "b", "c"};
public ObservableCollection<string> Crystallist = new ObservableCollection<string>{"aa", "bb", "cc"};
public ObservableCollection<Shape> Shapelist = new ObservableCollection<Shape>();
private SolidColorBrush _originalColorBrush = Brushes.Tomato;
private SolidColorBrush _selectedColorBrush;
private double _diameter;
public MainWindow()
{
_diameter = 50d;
this.ResizeMode = System.Windows.ResizeMode.CanMinimize;
Shapes = new ObservableCollection<Shape>();
InitializeComponent();
InitializeLists(Baseforms, CrystalGroups);
}
private void InitializeLists(ComboBox Baseforms, ComboBox CrystalGroups)
{
Baseforms.ItemsSource = Baselist;
CrystalGroups.ItemsSource = Crystallist;
Shape Circle = new Ellipse();
Circle.Stroke = System.Windows.Media.Brushes.Black;
Circle.Fill = System.Windows.Media.Brushes.DarkBlue;
Circle.HorizontalAlignment = HorizontalAlignment.Left;
Circle.VerticalAlignment = VerticalAlignment.Center;
Circle.Width = 50;
Circle.Height = 50;
Shapelist.Add(Circle);
}
private void UIElement_OnMouseDown(object sender, MouseButtonEventArgs e)
{
var inputElement = sender as IInputElement;
if (inputElement == null) return;
var point = e.GetPosition(inputElement);
Shape shape = new Ellipse
{
Stroke = Brushes.Black,
Fill = _originalColorBrush,
Width = _diameter,
Height = _diameter
};
var byX = point.X - _diameter / 2d;
var byY = point.Y - _diameter / 2d;
var existingShape = Shapes.FirstOrDefault(shapeToCheck =>
{
var data = shapeToCheck.Tag as ShapeDataPresentation;
if (data == null) return false;
var res = data.OriginalRectAroundShape.IntersectsWith(new Rect(point,point));
return res;
});
if (existingShape == null)
{
var shapeDataPresentation = new ShapeDataPresentation { Name = string.Format("Ox:{0}, Oy:{1}", point.X.ToString("##.###"), point.Y.ToString("##.###")), OriginalRectAroundShape = new Rect(new Point(byX, byY), new Size(_diameter, _diameter)) };
shape.Tag = shapeDataPresentation;
shape.ToolTip = new ToolTip{Content = shapeDataPresentation.Name};
var translateTransform = new TranslateTransform(byX, byY);
shape.RenderTransform = translateTransform;
Shapes.Add(shape);
}
else
{
if (DetailsList.SelectedItems.Contains(existingShape) == false)
{
DetailsList.SelectedItems.Clear();
DetailsList.SelectedItems.Add(existingShape);
}
}
}
private void Selector_OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var currentSelected = e.AddedItems.OfType<Shape>().ToList();
var prevSelected = e.RemovedItems.OfType<Shape>().ToList();
if (currentSelected.Count > 0)
{
currentSelected.ForEach(shape =>
{
_selectedColorBrush = Brushes.CadetBlue;
shape.Fill = _selectedColorBrush;
});
}
if (prevSelected.Count > 0)
{
prevSelected.ForEach(shape =>
{
shape.Fill = _originalColorBrush;
});
}
}
}
public class ShapeDataPresentation
{
public string Name { get; set; }
public Rect OriginalRectAroundShape { get; set; }
}
How it is looks like
Summary:
Here you can create item by mouse click on canvas, mouse down handled
in code (UIElement_OnMouseDown).
Allowed selection and multi-selection, each time you make selection it will be handled in code (Selector_OnSelectionChanged).
But it is better to use the MVVM based approach to work in wpf.
Regards.
I have to create many controls on runtime and i need to bind to an array, but they doesn't bind.
In the code we can assume that I get 3 nameplates, but when I type data into or select an item they don't set it in the property
View
Grid grid = new Grid();
int top = 10;
int position = 0;
foreach (NameplateModel nameplate in _nameplateList)
{
Label lbl = new Label();
lbl.Content = nameplate.FieldName;
lbl.HorizontalAlignment = HorizontalAlignment.Left;
lbl.VerticalAlignment = VerticalAlignment.Top;
lbl.Margin = new Thickness(0, top, 0, 0);
grid.Children.Add(lbl);
if (nameplate.LookupValue !=null && nameplate.LookupValue.Count() > 0)
{
ComboBox cbo = new ComboBox();
cbo.ItemsSource = nameplate.LookupValue;
Binding b = new Binding();
b.Source = DataContext;
b.Path = new PropertyPath("TextValues[0]");
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
b.Mode = BindingMode.TwoWay;
cbo.SetBinding(ComboBox.TextProperty,b);
if (!nameplate.LimitToList)
{
cbo.IsEditable = true;
}
cbo.HorizontalAlignment = HorizontalAlignment.Left;
cbo.Margin = new Thickness(98,top,0,0);
cbo.Width = 174;
cbo.VerticalAlignment = VerticalAlignment.Top;
grid.Children.Add(cbo);
}
else
{
TextBox txt = new TextBox();
Binding b = new Binding();
b.Source = DataContext;
b.Path = new PropertyPath(string.Format("TextValues[{0}]", position));
b.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;
b.Mode = BindingMode.TwoWay;
txt.SetBinding(TextBox.TextProperty, b);
txt.HorizontalAlignment = HorizontalAlignment.Left;
txt.VerticalAlignment = VerticalAlignment.Top;
txt.Width = 174;
txt.Margin = new Thickness(98, top, 0, 0);
grid.Children.Add(txt);
}
top = top + 29;
position++;
}
scvNameplate.Content = grid;
and my ViewModel
public string[] TextValues
{
get
{
return _textValues ?? (_textValues = new string[3]);
}
set
{
_textValues = value;
OnPropertyChanged("TextValues");
}
}
NameplateModel
public class NameplateModel
{
#region Fields
string _fieldName;
string _lookupCode;
string[] _lookupValue;
bool _limitToList;
#endregion
#region Properties
public string FieldName
{
get
{
return _fieldName;
}
set
{
_fieldName = value;
}
}
public string LookupCode
{
get
{
return _lookupCode;
}
set
{
_lookupCode = value;
}
}
public string[] LookupValue
{
get
{
return _lookupValue;
}
set
{
_lookupValue = value;
}
}
public bool LimitToList
{
get
{
return _limitToList;
}
set
{
_limitToList = value;
}
}
#endregion
}
I got more than I was expecting, First I remove all the code behind and putted in Xaml on the ItemsControl
View
<ItemsControl ItemsSource="{Binding NameplateList}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="100" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label Content="{Binding FieldName}" Margin="0,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Grid.Row="0" Grid.Column="0"/>
<ComboBox Text="{Binding TextValue, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding LookupValue}" IsEditable="{Binding LimitToList}" Margin="0,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="174" Grid.Row="0" Grid.Column="1" Visibility="{Binding IsVisible, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<TextBox Text="{Binding TextValue}" HorizontalAlignment="Left" VerticalAlignment="Top" Width="174" Margin="0,0,0,0" Visibility="{Binding IsVisibleText, Converter={StaticResource BooleanToVisibilityConverter}}" Grid.Row="0" Grid.Column="1"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
and then just need to add the string TextValue property to take the value I need into the NameplateModel instead of having a TextValue[] on the viewModel
Now it works perfectly, Thanks #BradleyDotNET