I want to show multiple image on a canvas. I need to position them differently in the canvas. I made a class for my images :
class MapItem:Image
{
public int DistanceToTop { get; set; }
public int DistanceToLeft { get; set; }
}
My XAML looks like this :
<UserControl.DataContext>
<Map:MapViewModel/>
</UserControl.DataContext>
<ItemsControl ItemsSource="{Binding All}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="Image">
<Setter Property="Canvas.Left" Value="{Binding DistanceToLeft}" />
<Setter Property="Canvas.Top" Value="{Binding DistanceToTop}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
My ViewModel used as DataContext :
class MapViewModel : ViewModelBase
{
public ObservableCollection<MapItem> All { get; set; }
public MapViewModel()
{
All = new ObservableCollection<MapItem>();
var wSource = new BitmapImage(new Uri(#"ImagePath"));
var wImage = new MapItem { Source = wSource, DistanceToLeft = 20, DistanceToTop = 20 };
test = wImage;
All.Add(wImage);
}
}
Why in the XAML my binding to DistanceToLeft and DistanceToTop are not working ?!?
Isn't it suppose to automatically look in the object use in my ObservableCollection ?
EDIT : I still have my problem. But now I know it's related with the Binding. I'm trying to implement all this using the MVVM pattern using the GalaSoft framework. So to start I set my DataContext to my MapViewModel. Why can't I acces the properties of the MapItem from my ObservableCollection ?
EDIT : Finally with the help of Clemens and Rachel I ended up with this.
My MapItem Class:
class MapItem:Image
{
public LatLon CoordMiddleOfImage { get; set; }
public LatLon CoordTopLeftOfImage { get; set; }
public int DistanceToTop
{
get { return (int) Canvas.GetTop(this); }
set { Canvas.SetTop(this, value); }
}
public int DistanceToLeft
{
get { return (int)Canvas.GetLeft(this); }
set { Canvas.SetLeft(this, value); }
}
public int ZOrder
{
get { return Panel.GetZIndex(this); }
set { Panel.SetZIndex(this, value); }
}
}
And my XAML like this :
<ItemsControl ItemsSource="{Binding All}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas ClipToBounds="True" SnapsToDevicePixels="True"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
It works like a charm for now :-)
I don't quite understand why after all you invented the DistanceToLeft and DistanceToTop properties and then struggle with binding. If you want to use Image controls as items, why not directly apply the attached properties Canvas.Left and Canvas.Top:
All = new ObservableCollection<Image>(); // no need for derived MapItem
var wSource = new BitmapImage(new Uri(#"ImagePath"));
var wImage = new Image { Source = wSource };
Canvas.SetLeft(wImage, 20);
Canvas.SetTop(wImage, 20);
All.Add(wImage);
Hence, no need for a Style:
<ItemsControl ItemsSource="{Binding All}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
However, you should consider to create a real ViewModel class that is not a control, something like this:
public class ImageItem
{
public string Source { get; set; }
public double Left { get; set; }
public double Top { get; set; }
}
Use it similar to your MapItem class
All = new ObservableCollection<ImageItem>();
ImageItem image = new ImageItem { Source = #"ImagePath", Left = 20, Top = 20 };
All.Add(image);
You would now define an ItemContainerStyle like this:
<ItemsControl.ItemContainerStyle>
<!-- ContentPresenter is the default item container in ItemsControl -->
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="{Binding Left}"/>
<Setter Property="Canvas.Top" Value="{Binding Top}"/>
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<Image Source="{Binding Source}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ItemsControl.ItemContainerStyle>
An ItemsControl wraps each item in a ContentPresenter, so the style in ItemContainerStyle is for the ContentPresenter, not the Image
If you remove TargetType="Image" from your style it should work fine
Related
My WPF app has a ViewModel that has an ObservableCollection that holds objects of type Item. Each Item has a color and a Rect that is drawn on the canvas:
Item Class:
public class Item
{
public Color ItemColor {get; set;}
public Rect ScaledRectangle {get; set;}
}
XAML:
<Grid>
<ItemsControl Name="Items" ItemsSource="{Binding Items, Mode=TwoWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<local:ItemView Visibility="Visible">
<local:ItemView.Background>
<SolidColorBrush Color="{Binding ItemColor}"/>
</local:ItemView.Background>
</local:ItemView>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style TargetType="{x:Type ContentPresenter}">
<Setter Property="Canvas.Left" Value="{Binding ScaledRectangle.Left}"/>
<Setter Property="Canvas.Top" Value="{Binding ScaledRectangle.Top}"/>
<Setter Property="FrameworkElement.Width" Value="{Binding ScaledRectangle.Width}"/>
<Setter Property="FrameworkElement.Height" Value="{Binding ScaledRectangle.Height}"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>
In my ViewModel, all I have to do is add a new Item to the ObservableCollection to draw it on the screen.
This works really well but now I find I need to change the ScaledRectangle property to some kind of collection. I want to modify this XAML to draw each rectangle in the ScaledRectangles collection. Can I modify this XAML so I can keep the ViewModel functionality to something like viewModel.AddNewItem(newItem)?
You must modify your ItemsView to support handling of a collection of Rect instead of a single Rect:
ItemsView.cs
public class ItemsView : Control
{
public Item DataSource
{
get => (Item)GetValue(DataSourceProperty);
set => SetValue(DataSourceProperty, value);
}
public static readonly DependencyProperty DataSourceProperty = DependencyProperty.Register(
"DataSource",
typeof(Item),
typeof(ItemsView),
new PropertyMetadata(default(Item), OnDataSourceChanged));
private Panel ItemsHost { get; set; }
private Dictionary<Rect, int> ContainerIndexTable { get; }
static ItemsView()
=> DefaultStyleKeyProperty.OverrideMetadata(typeof(ItemsView), new FrameworkPropertyMetadata(typeof(ItemsView)));
public ItemsView()
=> this.ContainerIndexTable = new Dictionary<Rect, int>();
private static void OnDataSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var this_ = d as ItemsView;
this_.UnloadRectangles(e.OldValue as Item);
this_.LoadRectangles(e.NewValue as Item);
}
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
this.ItemsHost = GetTemplateChild("PART_ItemsHost") as Panel;
LoadRectangles(this.DataSource);
}
private void UnloadRectangles(Item item)
{
if (item is null
|| this.ItemsHost is null)
{
return;
}
foreach (Rect rectangleDefinition in item.ScaledRectangles)
{
if (this.ContainerIndexTable.TryGetValue(rectangleDefinition, out int containerIndex))
{
this.ItemsHost.Children.RemoveAt(containerIndex);
}
}
}
private void LoadRectangles(Item item)
{
if (item is null
|| this.ItemsHost is null)
{
return;
}
foreach (Rect rectangleDefinition in item.ScaledRectangles)
{
var container = new Rectangle()
{
Height = rectangleDefinition.Height,
Width = rectangleDefinition.Width,
Fill = new SolidColorBrush(item.ItemColor)
};
Canvas.SetLeft(container, rectangleDefinition.Left);
Canvas.SetTop(container, rectangleDefinition.Top);
int containerIndex = this.ItemsHost.Children.Add(container);
_ = this.ContainerIndexTable.TryAdd(rectangleDefinition, containerIndex);
}
}
}
Gernic.xaml
<Style TargetType="local:ItemsView">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ItemsView">
<Canvas x:Name="PART_ItemsHost" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MainWindow.xaml
<ItemsControl>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:Item}">
<local:ItemsView DataSource="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Totally i am new to WPF, I need to solve the problem. Could anybody give me a sample xaml code without using code-behind.
Based on the questType (Check,radio, combo) Control need to be created based on the ObserverableCollection.
public class Order
{
public int OrderCode {get;set;}
public string Description {get;set;}
public ObserverableCollection<question> Questions{get;set;}
}
public class question
{
public string questType {get;set;}
public string Question {get;set;}
public ObserverableCollection<Answer> Answers {get;set;}
}
public class Answer
{
public string Ans{get; set;}
}
Based on the questType (Check,radio, combo)
Control need to be created based on the ObserverableCollection.
example:
1001 Pencil Gender? oMale oFemale oOther
[]Checkbox1 []Checkbox2
1002 Pen Fasting? oYes oNo
Here is how I would do it:
Code behind:
using System;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Windows;
namespace Ans
{
public class Order
{
public int OrderCode { get; set; }
public string Description { get; set; }
public ObservableCollection<Question> Questions { get; set; }
}
public class Question
{
public string questType { get; set; }
public string Label { get; set; }
public ObservableCollection<Answer> Answers { get; set; }
}
public class Answer
{
public string Ans { get; set; }
public bool IsSelected { get; set; }
}
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
#region Order
/// <summary>
/// Order Dependency Property
/// </summary>
public static readonly DependencyProperty OrderProperty =
DependencyProperty.Register("Order", typeof(Order), typeof(MainWindow),
new FrameworkPropertyMetadata((Order)null));
/// <summary>
/// Gets or sets the Order property. This dependency property
/// indicates ....
/// </summary>
public Order Order
{
get { return (Order)GetValue(OrderProperty); }
set { SetValue(OrderProperty, value); }
}
#endregion
public MainWindow()
{
InitializeComponent();
Order = new Order()
{
Questions = new ObservableCollection<Question>()
{
new Question()
{
questType = "Combo",
Label = "Combo",
Answers = new ObservableCollection<Answer>()
{
new Answer(){Ans = "Female"},
new Answer(){Ans = "Male"}
}
},
new Question()
{
questType = "Check",
Label = "Multi",
Answers = new ObservableCollection<Answer>()
{
new Answer(){Ans = "Female"},
new Answer(){Ans = "Male"}
}
},
new Question()
{
questType = "Radio",
Label = "Radio",
Answers = new ObservableCollection<Answer>()
{
new Answer(){Ans = "Female"},
new Answer(){Ans = "Male"}
}
}
}
};
DataContext = this;
}
private void Button_Click(object sender, RoutedEventArgs e)
{
foreach(Question q in Order.Questions)
{
Console.WriteLine( q.Label + " : " + string.Join(", " , q.Answers.Where(a=>a.IsSelected).Select(a=>a.Ans)) );
}
}
}
}
XAML:
<Window x:Class="Ans.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:Ans"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<DataTemplate x:Key="ComboQuestion">
<ComboBox ItemsSource="{Binding Answers}">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Ans}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</ComboBox.ItemContainerStyle>
</ComboBox>
</DataTemplate>
<DataTemplate x:Key="CheckQuestion">
<ItemsControl ItemsSource="{Binding Answers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox Content="{Binding Ans}" IsChecked="{Binding IsSelected, Mode=TwoWay}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
<DataTemplate x:Key="RadioQuestion">
<ItemsControl ItemsSource="{Binding Answers}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<RadioButton Content="{Binding Ans}" IsChecked="{Binding IsSelected, Mode=TwoWay}" GroupName="{Binding DataContext.Label, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Order.Questions}" Grid.IsSharedSizeScope="True">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" SharedSizeGroup="Label"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding Label}"/>
<ContentControl x:Name="ccQuestion" Grid.Column="1" Content="{Binding}" Margin="10"/>
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding questType}" Value="Combo">
<Setter TargetName="ccQuestion" Property="ContentTemplate" Value="{StaticResource ComboQuestion}"/>
</DataTrigger>
<DataTrigger Binding="{Binding questType}" Value="Check">
<Setter TargetName="ccQuestion" Property="ContentTemplate" Value="{StaticResource CheckQuestion}"/>
</DataTrigger>
<DataTrigger Binding="{Binding questType}" Value="Radio">
<Setter TargetName="ccQuestion" Property="ContentTemplate" Value="{StaticResource RadioQuestion}"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Button Content="Order" Click="Button_Click" VerticalAlignment="Bottom"/>
</Grid>
</Window>
The only thing I've added to your model is IsSelected property that allows to know if this answer was selected.
The other important thing is the Radios. Their GroupName property defines the scope. So if no GroupName is set then when clicking on a radio in one question it will unselect radio in another question. I used Question label in my solution however it only works if labels are unique.
Another point is that data triggers are OK if you have 3-5 question types and if thay are only based on the questionType. However for more complex scenarios you can look for ItemTemplateSelector. It allows to write C# code that will select the template based on each item in ItemsControl.
Let's say I have a questionnaire app, which consists of an ItemsControl with a list of controls each consisting of a Label and a ListBox. The items in each ListBox are checkboxes or radiobuttons or whatever.
My question is: When a checkbox is checked, how do I figure out which Question the checkbox applies to? Should I put a reference to the Question in the Tag property? If so, how would I do that?
The Tag binding code below doesn't work. It binds to the ListBoxItem. How do I bind it to the ItemsControl item?
MainWindow.xaml:
<Window x:Class="ListWithinListTest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<ResourceDictionary>
<Style x:Key="ConditionCheckBoxListStyle" TargetType="{x:Type ListBox}">
<Setter Property="SelectionMode" Value="Multiple" />
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListBoxItem}" >
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<CheckBox IsChecked="{Binding IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
Click="CheckBoxClicked"
Tag="{Binding RelativeSource={RelativeSource TemplatedParent}}"
>
<ContentPresenter></ContentPresenter>
</CheckBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</Window.Resources>
<Grid>
<ItemsControl Name="QuizControl" ItemsSource="{Binding QuizQuestions}" ScrollViewer.CanContentScroll="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel Margin="10 0 10 10" VerticalAlignment="Top">
<Label Content="{Binding Text}" />
<ListBox ItemsSource="{Binding Options}"
DisplayMemberPath="Text"
Tag="{Binding RelativeSource={RelativeSource AncestorType=ItemsControl}}"
Style="{StaticResource ConditionCheckBoxListStyle}"
/>
</StackPanel>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</Window>
MainWindow.xaml.cs:
using System.Windows;
using System.Windows.Controls;
namespace ListWithinListTest
{
public class Option
{
public string Text { get; set; }
public bool IsSelected { get; set; } = false;
}
public class Question
{
public string Text { get; set; }
public Option[] Options { get; set; }
}
public class ViewModel
{
public Question[] QuizQuestions { get; set; }
public ViewModel()
{
QuizQuestions = new Question[] {
new Question { Text = "How are you?", Options = new Option[] { new Option { Text = "Good" }, new Option { Text = "Fine" } } },
new Question { Text = "How's your dog?", Options = new Option[] { new Option { Text = "Sleepy" }, new Option { Text = "Hungry" } } },
};
}
}
public partial class MainWindow : Window
{
private ViewModel viewModel;
public MainWindow()
{
InitializeComponent();
this.DataContext = viewModel = new ViewModel();
}
private void CheckBoxClicked(object sender, RoutedEventArgs e)
{
Question question = viewModel.QuizQuestions[???];
}
}
}
Ok, I messed with the Live Visual Tree window until, by process of elimination, I realized that the ListBox is what the Question is going to be bound to. I know now that's a big duh, but that's where I am with this. Then I used RelativeSource AncestorType to find it and the DataSource property to get the question:
MainWindow.xaml:
<CheckBox IsChecked="{Binding IsSelected,RelativeSource={RelativeSource TemplatedParent},Mode=TwoWay}"
Click="CheckBoxClicked"
Tag="{Binding RelativeSource={RelativeSource AncestorType=ListBox}}"
>
MainWindow.xaml.cs:
private void CheckBoxClicked(object sender, RoutedEventArgs e)
{
CheckBox checkBox = (CheckBox)sender;
ListBox listBox = (ListBox)checkBox.Tag;
Question question = (Question)listBox.DataContext;
Debug.WriteLine(question.Text);
}
You have binded QuizQuestions to the QuizControl, you can get it back from the ItemsSource property.
var questions = (Question[]) QuizControl.ItemsSource;
EDIT
It looks like you got the answer yourself, just another way I would like to suggest to your original question:
Create one more property to your Option class
public class Option
{
public string Text { get; set; }
public bool IsSelected { get; set; } = false;
public int Index{ get; set; }
}
And then add Index to each of your question options.
QuizQuestions = new Question[] {
new Question { Text = "How are you?", Options = new Option[] { new Option { Text = "Good", Index = 0 }, new Option { Text = "Fine", Index = 0 } } },
new Question { Text = "How's your dog?", Options = new Option[] { new Option { Text = "Sleepy", Index = 1 }, new Option { Text = "Hungry", Index = 1 } } },
};
In your CheckBox event you can get the Option Index
private void CheckBoxClicked(object sender, RoutedEventArgs e)
{
var s = (CheckBox)sender;
var op = (Option)s.Tag;
Question question = viewModel.QuizQuestions[op.Index];
}
In my WPF application, I need to post n number of my user control in in form of rows and columns. My user control is like
The number of rows can be n, while number of columns will be either 1 or 2 depending on the view user chooses to use.
Here is the collection that contains my UserControls
private Collection<TemplateView> _templates;
public Collection<TemplateView> Templates { get { return _templates; } set { _templates = value; } }
And here is the XAML code that I used.
<ItemsControl ItemsSource="{Binding Templates}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="{Binding NumColumns}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Grid.Column" Value="{Binding ColumnIndex}" />
<Setter Property="Grid.Row" Value="{Binding RowIndex}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<v:TemplateView Content="{Binding }" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
v:TemplateView is the UserControl whose n copied needs to be posted in rows/columns.
Here is some of the XAML showing binding of UserControl's controls.
<Label Content="{Binding Title, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
<Label Content="{Binding Type, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
<TextBlock><Hyperlink Command="{Binding DetailsViewCommand}">Details</Hyperlink>
</TextBlock>
<TextBlock><Hyperlink Command="{Binding AddCommand}">Add</Hyperlink>
And here is my UserControl's VIewModel's code
private ICommand _detailsViewCommand;
public ICommand DetailsViewCommand { get { return _detailsViewCommand ?? (_detailsViewCommand = new RelayCommand(DetailsView)); } }
public void DetailsView()
{
}
private ICommand _addCommand;
public ICommand AddCommand { get { return _addCommand ?? (_addCommand = new RelayCommand(Add)); } }
private void Add()
{
}
private string _layerType;
public string LayerType
{
get { return _layerType; }
set { _layerType = value; }
}
private string _title;
public string Title
{
get { return _title; }
set { _title = value; }
}
All copies of this UserControl will carry different information in labels and Image. So I need to know in the userControl's ViewModel, which UserControls (or which item in Templates Collection) has been clicked when user presses the Details button.
Above XAML and code does not tell me which item/user control was clicked on Details button click. So how should I accomplish the two tasks?
I'm relatively new to WPF and am having a hard time binding a custom class to a datagrid. While the text properties are OK (they are read-only anyway), the togglebuttons for the boolean properties are not updated in my item list, and they are also not displayed according to the values set initially. They do however respond correctly to clicks in the UI.
<Style x:Key="ToggleImageStyleBien" TargetType="ToggleButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<Image Name="img" Source="Images/transp.png"/>
</StackPanel>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsChecked" Value="True">
<Setter TargetName="img" Property="Source" Value="Images/good.png"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Here's the DataGrid itself:
<DataGrid AutoGenerateColumns="False" Height="Auto" Name="dataGridRevision" Width="Auto" Margin="6,6,6,0" ItemsSource="{Binding}" VerticalScrollBarVisibility="Auto" VerticalGridLinesBrush="{x:Null}">
<DataGrid.Columns>
<DataGridTextColumn Header="Code" Binding="{Binding Code}" Visibility="Collapsed"/>
<DataGridTextColumn Header="DescripciĆ³n" Binding="{Binding Description}" IsReadOnly="True"/>
<DataGridTemplateColumn Header="Bien">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ToggleButton Style="{StaticResource ToggleImageStyleBien}" Click="ToggleButton_Click" IsChecked="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=ReviewItem.Good, Mode=TwoWay}" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Comentario" />
</DataGrid.Columns>
</DataGrid>
This is the class I'm binding to:
public class ReviewItem
{
public string Code { set; get; }
public string Description { set; get; }
public bool Good { set; get; }
public bool Bad { set; get; }
public string Comment { set; get; }
}
As far as I can tell, I'm not using the right Binding property in the ToggleButton, but I have tried a lot and have run out of ideas. The list properties are not changing on click, the values are not displayed according to the data.
Please help...
Thanks!
Joerg.
Changed the class to this, based on other examples found:
public class ReviewItem : INotifyPropertyChanged
{
public string Code { set; get; }
public string Description { set; get; }
public bool Bad { set; get; }
public string Comment { set; get; }
private bool _isChecked;
public bool Good
{
get { return _isChecked; }
set
{
System.Diagnostics.Debug.WriteLine("Good = " + value);
_isChecked = value;
OnPropertyChanged("Good");
}
}
private void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
public event PropertyChangedEventHandler PropertyChanged;
}
You are currently binding to UserControl.ReviewItem, which is not a valid property on a UserControl.
Try binding to DataContext.ReviewItem instead which will bind to UserControl.DataContext.ReviewItem
If you do not mean to bind to UserControl.DataContext.ReviewItem.Good, and instead want to bind to DataGrid.ReviewItems[x].Good, then you only need to bind IsChecked="{Binding Good}". This is because the default DataContext on a DataGridCell is the row's data item, so if your collection contains a list of ReviewItems, the DataContext of the cell is already set to a ReviewItem
Also, I would highly recommend using Snoop for debugging WPF bindings. You can use it on your application to see what the DataContext is for individual UI Elements, and figure out if your bindings are correct or not.