I want to have function in WPF:Double click on row in Data Grid will show
cells in window. How can I do it that?I programm so but I never have double click event. Can somebody help me?
Thanks.
<DataGrid AutoGenerateColumns="False" HorizontalAlignment="Left"Margin="14,55,0,46"
Name="dataGridCustomers" Width="575" ItemsSource=" {Binding Path=LoadDataBinding}"
CanUserResizeRows="False">
<DataGrid.RowStyle>
<Style TargetType="{x:Type DataGridRow}">
<EventSetter Event="MouseDoubleClick" Handler="dataGridCustomers_MouseDoubleClick"/>
</Style>
</DataGrid.RowStyle>
<DataGrid.Columns>
<DataGridTextColumn Binding="{Binding Path=DEPARTMENT_ID}" Header="DepartmentID" Width="100"
IsReadOnly="True" />
<DataGridTextColumn Binding="{Binding Path=DEPARTMENT_NAME}" Header="Department name" Width="100"
IsReadOnly="True" />
<DataGridTextColumn Binding="{Binding Path=LOCATION}" Header="Location" Width="150"
IsReadOnly="True" />
</DataGrid.Columns>
</DataGrid>
Try this
So, for instance, your XAML might look something like this:
<SomeControl MouseDown="MyMouseDownHandler">
...
</SomeControl>
code behind the click event..
private void MyMouseDownHandler(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
//Handle double-click
}
}
Just create your own dependency property DataGridDoubleClickProperty where you attach handler for DataGrid.MouseDoubleClick event
public static class Commands
{
public static readonly DependencyProperty DataGridDoubleClickProperty =
DependencyProperty.RegisterAttached("DataGridDoubleClickCommand", typeof ( ICommand ), typeof ( Commands ),
new PropertyMetadata(new PropertyChangedCallback(AttachOrRemoveDataGridDoubleClickEvent)));
public static ICommand GetDataGridDoubleClickCommand(DependencyObject obj)
{
return (ICommand) obj.GetValue(DataGridDoubleClickProperty);
}
public static void SetDataGridDoubleClickCommand(DependencyObject obj, ICommand value)
{
obj.SetValue(DataGridDoubleClickProperty, value);
}
public static void AttachOrRemoveDataGridDoubleClickEvent(DependencyObject obj, DependencyPropertyChangedEventArgs args)
{
DataGrid dataGrid = obj as DataGrid;
if ( dataGrid != null )
{
ICommand cmd = (ICommand) args.NewValue;
if ( args.OldValue == null && args.NewValue != null )
{
dataGrid.MouseDoubleClick += ExecuteDataGridDoubleClick;
}
else if ( args.OldValue != null && args.NewValue == null )
{
dataGrid.MouseDoubleClick -= ExecuteDataGridDoubleClick;
}
}
}
private static void ExecuteDataGridDoubleClick(object sender, MouseButtonEventArgs args)
{
DependencyObject obj = sender as DependencyObject;
ICommand cmd = (ICommand) obj.GetValue(DataGridDoubleClickProperty);
if ( cmd != null )
{
if ( cmd.CanExecute(obj) )
{
cmd.Execute(obj);
}
}
}
}
In your View you use Binding to map this DependencyProperty to Command
<Grid DataContext="{StaticResource viewModel}">
<DataGrid AutoGenerateColumns="True"
ItemsSource="{Binding Data}"
SelectedItem="{Binding SelectedItem}"
clr:Commands.DataGridDoubleClickCommand="{Binding DataGridDoubleClick}"
/>
</Grid>
DataGridDoubleClick is ICommand property in your ViewModel class
You can subscribe on PreviewMouseDoubleClick event.
<Style TargetType="DataGridRow">
<EventSetter Event="PreviewMouseDoubleClick" Handler="OnDoubleClick"/>
</Style>
Handler:
private void OnDoubleClick(object sender, MouseButtonEventArgs e)
{
e.Handled = true;
System.Diagnostics.Debug.WriteLine("DoubleClick!");
}
Related
I have a Datagrid in WPF in which first column has checkbox column and last column has buttons.
Initially, I want to make all the buttons disabled and whenever any checkbox is checked then button of that row should get enabled.
checkbox is unchecked then button should be disabled.
Searched a Lot but could not find anything related to this.
I am not using MVVM.. How to do this on the code behind?
Thanks
This is my Xaml Code and I am simply assigning my itemsource on the code behind
<Grid Grid.Row="2" Grid.ColumnSpan="2" Margin="5" VerticalAlignment="Top" HorizontalAlignment="Center" >
<Border BorderThickness="0" Margin="10" CornerRadius="15">
<Border.BitmapEffect>
<DropShadowBitmapEffect />
</Border.BitmapEffect>
<Grid>
<Border x:Name="BDRounded" BorderThickness="0" CornerRadius="15" Background="White"/>
<DataGrid HorizontalAlignment="Left" x:Name="dgrdActors" RowHeight="74" AutoGenerateColumns="False" CanUserAddRows="False"
BorderThickness="1,0,0,0" BorderBrush="#FFD1A251" FontSize="28" Foreground="#DCA566" FontFamily="Helvetica Neue"
CanUserResizeRows="False" AlternatingRowBackground="Linen" AlternationCount="2" Background="#DCA566"
RowHeaderWidth="0" CanUserResizeColumns="False" CanUserSortColumns="False" CanUserReorderColumns="False"
ScrollViewer.CanContentScroll="True" ScrollViewer.VerticalScrollBarVisibility="Visible"
HorizontalGridLinesBrush="#FFD1A251" VerticalGridLinesBrush="#FFD1A251" Height="326"
SelectionMode="Extended" SelectionUnit="FullRow" VirtualizingStackPanel.VirtualizationMode="Standard"
Style="{StaticResource DatagridStyle}">
<DataGrid.Columns>
<DataGridTemplateColumn Width="70" CanUserReorder="False" CanUserResize="False" CanUserSort="False" CellStyle="{StaticResource HitVisibilityCellStyle}" HeaderStyle="{StaticResource HeaderStyle}" >
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Viewbox Margin="-1">
<!--<CheckBox x:Name="chkboxactors" HorizontalAlignment="Center" VerticalAlignment="Center"
IsChecked="{Binding IsActorChecked, UpdateSourceTrigger=PropertyChanged}"></CheckBox>-->
<CheckBox x:Name="chkboxActors" HorizontalAlignment="Center" VerticalAlignment="Center"></CheckBox>
</Viewbox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Header="Actor Name(s)" Width="300" Binding="{Binding ActorName}" CanUserReorder="False" CellStyle="{StaticResource CellStyle}" CanUserResize="False" CanUserSort="False" HeaderStyle="{StaticResource HeaderStyle}" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="HorizontalAlignment" Value="Center"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTextColumn Header="Role(s)" Width="300" Binding="{Binding Role}" CanUserReorder="False" CellStyle="{StaticResource CellStyle}" CanUserResize="False" CanUserSort="False" HeaderStyle="{StaticResource HeaderStyle}" IsReadOnly="True">
<DataGridTextColumn.ElementStyle>
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="HorizontalAlignment" Value="Center"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
</DataGridTextColumn.ElementStyle>
</DataGridTextColumn>
<DataGridTemplateColumn Width="250" CanUserReorder="False" CanUserResize="False" HeaderStyle="{StaticResource HeaderStyle}" CellStyle="{StaticResource HitVisibilityCellStyle}">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button x:Name="btnSelectRole" Content="Select Role" Style="{StaticResource DatagridButtonStyle}"></Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
<DataGrid.OpacityMask>
<VisualBrush Visual="{Binding ElementName=BDRounded}"/>
</DataGrid.OpacityMask>
</DataGrid>
</Grid>
</Border>
</Grid>
Here you go!
There's no need to do the enabling/disabling in the ViewModel, as this can all be done in XAML.
XAML:
<DataGrid ItemsSource="{Binding Items}" AutoGenerateColumns="False">
<DataGrid.Columns>
<DataGridCheckBoxColumn Binding="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" />
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="100">
<Button.Style>
<Style TargetType="Button">
<Setter Property="IsEnabled" Value="false"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked}" Value="true">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
ViewModel:
public class ViewModel
{
public List<Data> Items { get; private set; }
public ViewModel()
{
Items = new List<Data>
{
new Data(),
new Data(),
new Data()
};
}
}
public class Data : INotifyPropertyChanged
{
private bool _isChecked;
public bool IsChecked
{
get {return _isChecked; }
set
{
_isChecked = value;
OnPropertyChanged("IsChecked");
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string property)
{
var handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(property));
}
}
}
Edit:
Since you've requested a code-behind implementation, here you go. This works by traversing the visual tree based on the current row that the checkbox was clicked from.
XAML:
<Window x:Class="WpfApplication1.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">
<DataGrid AutoGenerateColumns="False" x:Name="MyDataGrid">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox Click="CheckBox_Clicked"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Width="100" x:Name="Button" IsEnabled="false" />
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</Window>
XAML.CS:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
MyDataGrid.ItemsSource = new List<string>
{
"test",
"test1",
"test2",
"test3"
};
}
private void CheckBox_Clicked(object sender, RoutedEventArgs e)
{
var checkBox = sender as CheckBox;
if (checkBox != null)
{
var associatedRow = VisualTreeHelper.GetParent(checkBox);
while ((associatedRow != null) && (associatedRow.GetType() != typeof(DataGridRow)))
{
associatedRow = VisualTreeHelper.GetParent(associatedRow);
}
var dataGridRow = associatedRow as DataGridRow;
if (dataGridRow != null)
{
var associatedButton = FindChild(dataGridRow, "Button");
if (associatedButton != null)
{
associatedButton.IsEnabled = checkBox.IsChecked.HasValue ? checkBox.IsChecked.Value : false;
}
}
}
}
public static Button FindChild(DependencyObject parent, string childName)
{
if (parent == null) return null;
Button foundChild = null;
var childrenCount = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < childrenCount; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
var childType = child is Button;
if (!childType)
{
foundChild = FindChild(child, childName);
if (foundChild != null) break;
}
else if (!string.IsNullOrEmpty(childName))
{
var frameworkElement = child as FrameworkElement;
if (frameworkElement != null && frameworkElement.Name == childName)
{
foundChild = (Button)child;
break;
}
}
else
{
foundChild = (Button)child;
break;
}
}
return foundChild;
}
}
If the datagrid is bound to a collection of objects you own (ideally in this case a Facade of a model), then add a IsSelected property to the object that makes up the collection. You can databind your checkbox to that property.
To enable/disable the button, have the model/facade in the collection implement ICommand. You can then use the CanExecute method to enable/disable the button based on the value of IsSelected.
public class User : ICommand, INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public event EventHandler CanExecuteChanged
{
add
{
CommandManager.RequerySuggested += value;
}
remove
{
CommandManager.RequerySuggested -= value;
}
}
public bool IsSelected
{
get
{
return this.isSelected;
}
set
{
this.isSelected = value;
CommandManager.InvalidateRequerySuggested();
this.OnPropertyChanged("IsSelected");
}
}
public bool CanExecute(object parameter)
{
return this.IsSelected;
}
public void Execute(object parameter)
{
// ... Do stuff ...
}
private void RaiseCanExecuteChanged()
{
var handler = this.CanExecuteChanged;
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(property));
}
private void OnPropertyChanged(string property)
{
var handler = this.PropertyChanged;
if (handler == null)
{
return;
}
handler(this, new PropertyChangedEventArgs(property));
}
}
Now you bind your checkbox to the IsSelected property. Anytime that the checkbox is selected, the CanExecute method will fire on the class.
Ideally you would use a DelegateCommand class from either MVVMLight or Prism, which have a RaiseCanExecuteChanged() method. This lets you avoid using the CommandManager to requery it.
This can be your DataGrid definition:
<DataGrid x:Name="TestDataGrid" ItemsSource="{Binding source}" AutoGenerateColumns="False" CanUserAddRows="False">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<CheckBox x:Name="TestBox" Content="Test" IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Button Content="Click" IsEnabled="{Binding IsChecked}"/>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Pretty simple code behind:
public partial class MainWindow : Window
{
public ObservableCollection<Model> source { get; set; }
public MainWindow()
{
InitializeComponent();
source = new ObservableCollection<Model>();
source.Add(new Model());
source.Add(new Model());
this.DataContext = this;
}
}
This could be your model:
public class Model : DependencyObject
{
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
// Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsCheckedProperty =
DependencyProperty.Register("IsChecked", typeof(bool), typeof(Model), new PropertyMetadata(false));
}
Or implement INPC inteface:
public class Model : INotifyPropertyChanged
{
private bool _IsChecked;
public bool IsChecked
{
get { return _IsChecked; }
set
{
_IsChecked = value;
PropertyChanged(this, new PropertyChangedEventArgs("IsChecked"));
}
}
public event PropertyChangedEventHandler PropertyChanged = delegate { };
}
< DataGridTemplateColumn Header="{ Loc CellSettings_Min}" >
<DataGridTemplateColumn.CellTemplate >
< DataTemplate >
< TextBox Width="100" Text="{ Binding Interval }" >
<TextBox.Style>
< Style TargetType = "TextBox" >
< Setter Property="IsEnabled" Value="false" />
< Style.Triggers >
< DataTrigger Binding = "{ Binding AutoScale }" Value= "True" >
< Setter Property = "IsEnabled" Value = "False" />
< /DataTrigger >
< DataTrigger Binding = "{ Binding AutoScale }" Value="False" >
< Setter Property = "IsEnabled" Value = "True" />
</ DataTrigger >
</ Style.Triggers >
</ Style >
</ TextBox.Style >
</ TextBox >
</ DataTemplate >
</ DataGridTemplateColumn.CellTemplate >
</ DataGridTemplateColumn >
You should use MVVM.DataBinding is convenient .
Xaml
<CheckBox Name="checkbox" IsChecked="{Binding Checked, Mode = TwoWay}" />
<Button IsEnabled="{Binding ButtonEnabled , Mode = TwoWay}" />
C#
In ViewModel
public class ViewMode : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _buttonChecked = false;
public bool ButtonChecked
{
get
{
return _buttonChecked;
}
set
{
if(value == true)
{
_buttonChecked = value;
OnPropertyChanged("ButtonChecked");
}
}
}
private bool _checked;
public bool Checked
{
get
{
return _checked;
}
set
{
if(value == true)
{
_checked= value;
ButtonChecked = value;
OnPropertyChanged("Checked");
}
}
}
[NotifyPropertyChangedInvocator]
private virtual void OnPropertyChanged(string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Can anyone help me with this problem , because i read a blog how to do this on
Walkthrough: Two-way binding inside a XAML User Control
but i don't now how to do this with a bool value
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValueIsSelected(IsSelectedProperty, value); }
}
private void SetValueIsSelected(DependencyProperty property, object value,
[System.Runtime.CompilerServices.CallerMemberName] bool s = null)
{
SetValue(property, value);
if (PropertyChanged != null)
{
string sender = s.ToString();
PropertyChanged(this, new PropertyChangedEventArgs(sender));
}
}
// Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(CustomPolygon), new PropertyMetadata(0));
So this was wrong accordingly #Clemens
to understand it more here i some more information on my application
In my MainWindow i am using two ComboBoxes to fill my data with binding to a public ObservableCollection DataPlannen
My code behind MainWindow:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private ObservableCollection<Plan> dataPlannen;
public ObservableCollection<Plan> DataPlannen
{
get { return dataPlannen; }
set
{
if (value != dataPlannen)
{
dataPlannen = value;
}
}
}
//non relevant code deleted
//get database data for ComboBoxes
private void btnGetPlanData_Click(object sender, RoutedEventArgs e)
{
try
{
this.Cursor = Cursors.Wait;
DataPlannen = new PlanCanvasModel().PlanHotspotsHardwares;
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString();
}
finally
{
this.Cursor = Cursors.Arrow;
}
}
private void cmbHotspot_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (cmbHotspot.SelectedIndex != -1)
{
foreach (Hotspot hotspot in cmbHotspot.ItemsSource)
{
if (hotspot == (Hotspot)cmbHotspot.SelectedItem)
{
hotspot.IsSelected = true;
else
{
hotspot.IsSelected = false;
}
}
}
}
My MainWindow XAML:
<Grid Background="LightGray">
<DockPanel Name="TestCanvas" LastChildFill="True">
<Grid x:Name="Sidebar" DockPanel.Dock="Right" Width="200">
<StackPanel Margin="0,10,10,0">
<ScrollViewer Height="112" VerticalScrollBarVisibility="Auto">
<StackPanel>
<StackPanel Orientation="Horizontal" Margin="0,0,0,5">
<Button x:Name="btnGetPlanData" Width="30" Height="30" HorizontalAlignment="Left" Margin="5,0" Click="btnGetPlanData_Click">
<Image Source="Images/database38.png" Stretch="Uniform"></Image>
<Button.ToolTip>Laad plannen in</Button.ToolTip>
</Button>
</StackPanel>
<Grid>
<ComboBox x:Name="cmbPlannen" Width="180"
ItemsSource="{Binding ElementName=myWindow,Path=DataPlannen}"
DisplayMemberPath="{Binding Plan_naam}"
SelectedValuePath="{Binding Plan_Id}"
SelectionChanged="cmbPlannen_SelectionChanged" IsEditable="True">
<ComboBox.ToolTip>
<ToolTip>Zoek op text of selecteer</ToolTip>
</ComboBox.ToolTip>
</ComboBox>
</Grid>
</StackPanel>
</ScrollViewer>
<Grid>
<ComboBox Margin="5" SnapsToDevicePixels="True" ItemsSource="{Binding ElementName=cmbPlannen,Path=SelectedItem.Hotspots,Mode=TwoWay}"
x:Name="cmbHotspot" SelectedIndex="0"
DisplayMemberPath="{Binding Hotspot_naam}"
SelectedValuePath="{Binding Hotspot_Id}"
SelectedItem="{Binding SelectedItem}"
SelectionChanged="cmbHotspot_SelectionChanged" IsEditable="True">
<ComboBox.ItemContainerStyle>
<Style>
<Setter Property="Control.Padding" Value="0"></Setter>
<Style.Triggers>
<Trigger Property="ComboBoxItem.IsSelected" Value="True">
<Setter Property="ComboBoxItem.Background" Value="LightGray" />
</Trigger>
<Trigger Property="ComboBoxItem.IsHighlighted" Value="True">
<Setter Property="ComboBoxItem.Background" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBox.ToolTip>
<ToolTip>Zoek op text of selecteer</ToolTip>
</ComboBox.ToolTip>
</ComboBox>
</Grid>
</StackPanel>
</Grid>
<Grid x:Name="MainContainer" ClipToBounds="True"
>
<Viewbox>
<ItemsControl x:Name="drawingsheet" ItemsSource="{Binding ElementName=cmbPlannen, Path=SelectedItem.Hotspots}"
Width="{StaticResource canvasWidth}"
Height="{StaticResource canvasHeight}"
MouseLeftButtonUp="drawingsheet_MouseLeftButtonUp"
MouseRightButtonDown="drawingsheet_MouseRightButtonDown"
MouseWheel="drawingsheet_MouseWheel"
MouseLeftButtonDown="drawingsheet_MouseLeftButtonDown"
MouseMove="drawingsheet_MouseMove"
Background="White"
>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas Background="{Binding ElementName=cmbPlannen,Path=SelectedItem.Plan_image,Converter={StaticResource ResourceKey=bytesToBitmapImageConverter}}"
Width="{StaticResource canvasWidth}" Height="{StaticResource canvasHeight}" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<customPolygon:CustomPolygon x:Name="currentPolygon" PointsPolygon="{Binding Hotspot_points}"
IsSelected="{Binding IsSelected,Mode=TwoWay}"
HasChildren="{Binding HasChildren}"></customPolygon:CustomPolygon>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemContainerStyle>
<Style>
<Setter Property="Canvas.Top" Value="{Binding Path=Y}" />
<Setter Property="Canvas.Left" Value="{Binding Path=X}" />
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.RenderTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleCanvas"></ScaleTransform>
<TranslateTransform x:Name="moveCanvas"></TranslateTransform>
</TransformGroup>
</ItemsControl.RenderTransform>
</ItemsControl>
</Viewbox>
</Grid>
</DockPanel>
</Grid>
My UserControl CustomPolygon.Xaml:
<UserControl x:Class="testCanvas.Controls.DrawingControls.CustomPolygon"
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"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Name="userControl"
>
<Polygon x:Name="polygon"
Points="{Binding ElementName=userControl,Path=PointsSource}"
StrokeThickness="0.5"
Stroke="Black"
Opacity="0.5"
MouseEnter="polygon_MouseEnter"
MouseLeave="polygon_MouseLeave"
MouseLeftButtonDown="polygon_MouseLeftButtonDown"/>
</UserControl>
My code behind from CustomPolygon
public partial class CustomPolygon : UserControl, INotifyPropertyChanged
{
public CustomPolygon()
{
InitializeComponent();
}
#region Properties
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set
{
SetValue(IsSelectedProperty, value);
if (IsSelected)
{
polygon.Stroke = Brushes.Red;
polygon.StrokeThickness = 2;
}
else
{
polygon.Stroke = Brushes.Black;
polygon.StrokeThickness = 0.5;
}
}
}
// Using a DependencyProperty as the backing store for IsSelected. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register("IsSelected", typeof(bool), typeof(CustomPolygon),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
//points for polygon
public PointCollection PointsPolygon
{
get { return (PointCollection)GetValue(PointsPolygonProperty); }
set { SetValue(PointsPolygonProperty, value); }
}
// Using a DependencyProperty as the backing store for PointsPolygon. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PointsPolygonProperty =
DependencyProperty.Register("PointsPolygon", typeof(PointCollection), typeof(CustomPolygon),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public bool HasChildren
{
get { return (bool)GetValue(HasChildrenProperty); }
set { SetValue(HasChildrenProperty, value); }
}
// Using a DependencyProperty as the backing store for HasChildren. This enables animation, styling, binding, etc...
public static readonly DependencyProperty HasChildrenProperty =
DependencyProperty.Register("HasChildren", typeof(bool), typeof(CustomPolygon),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
#endregion
private void polygon_MouseEnter(object sender, MouseEventArgs e)
{
if (IsSelected != true)
{
polygon.Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString(Properties.Settings.Default.HotspotHover));
polygon.StrokeThickness = 1;
}
}
private void polygon_MouseLeave(object sender, MouseEventArgs e)
{
if (IsSelected != true)
{
polygon.Stroke = Brushes.Black;
polygon.StrokeThickness = 0.5;
}
}
private void polygon_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
IsSelected = !IsSelected;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
Problem is now if i change my selection in cmbHotspot the property is not changing in my usercontrol and visa versa
What they are telling in this blog is not correct for WPF. The article is not about WPF and what it shows is not even necessary in Windows Runtime. You should definitely ignore the parts about setting the DataContext in the constructor of a custom control.
Anyway, you must not call anything else than SetValue in the setter of the CLR wrapper of a dependency property. See the XAML Loading and Dependency Properties article on MSDN for details.
Besides that a dependency property does not need to raise a PropertyChanged event, so your SetValueIsSelected property is redundant anyway.
Finally, your property metadata was wrong, because 0 is not a valid default value for type bool.
Your declaration should look like this:
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(CustomPolygon));
public bool IsSelected
{
get { return (bool)GetValue(IsSelectedProperty); }
set { SetValue(IsSelectedProperty, value); }
}
Unless you don't want to set any non-standard property metadata, you don't need to specify the PropertyMetadata parameter of the Register method.
However, if you want that the property binds two-way by default, you have to set the appropriate flag by property metadata.
public static readonly DependencyProperty IsSelectedProperty =
DependencyProperty.Register(
"IsSelected", typeof(bool), typeof(CustomPolygon),
new FrameworkPropertyMetadata(
false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault);
I have a class that has a boolean property called IsChecked.
A collection of this class exist in my viewmodel. I've bound a datagrid in my view to this collection. I need to call a method in my viewmodel when the checkbox in the view gets changed. I've implemented INotifyPropertyChanged on the class and it is firing when I check the box but I don't know how to call the method in my viewmodel.
Here's the class in my model...
public class AccountComponent : INotifyPropertyChanged
{
public string Name { get; set; }
public decimal Amount { get; set; }
private bool _isChecked;
public bool IsChecked
{
get { return _isChecked; }
set
{
_isChecked = value;
NotifyPropertyChanged("IsChecked");
}
}
public bool Enabled { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
}
Here's the collection in my viewmodel...
private ObservableCollection<AccountComponent> _accountComponents;
private string _accountStatus;
public ObservableCollection<AccountComponent> AccountComponents
{
get { return _accountComponents; }
set
{
_accountComponents = value;
NotifyPropertyChanged("AccountComponents");
CalculateComponentTotal();
}
}
Here's my XAML in the view...
<DataGrid ItemsSource="{Binding AccountComponents}" AutoGenerateColumns="False" Margin="5">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Grid>
<CheckBox IsChecked="{Binding IsChecked, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" IsEnabled="{Binding Enabled}"/>
</Grid>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
<DataGridTextColumn Binding="{Binding Name}" Header="Component" Width="*" IsReadOnly="True" ElementStyle="{DynamicResource TextBlock-Sketch}"/>
<DataGridTextColumn Binding="{Binding Amount,StringFormat={}{0:C}}" IsReadOnly="True" Header="Charge" ElementStyle="{DynamicResource TextBlock-Sketch}">
<DataGridTextColumn.CellStyle>
<Style>
<Setter Property="TextBlock.TextAlignment" Value="Right"/>
</Style>
</DataGridTextColumn.CellStyle>
</DataGridTextColumn>
</DataGrid.Columns>
</DataGrid>
Since AccountComponent implements INPC you can observe the IsChecked property in your VM.
say in your VM constructor:
AccountComponents = new ObservableCollection<AccountComponent>();
AccountComponents.CollectionChanged += AccountComponentsOnCollectionChanged;
...
private void AccountComponentsOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs args) {
if (args.NewItems != null && args.NewItems.Count != 0)
foreach (AccountComponent account in args.NewItems)
account.PropertyChanged += AccountOnPropertyChanged;
if (args.OldItems != null && args.OldItems.Count != 0)
foreach (AccountComponent account in args.OldItems)
account.PropertyChanged -= AccountOnPropertyChanged;
}
private void AccountOnPropertyChanged(object sender, PropertyChangedEventArgs args) {
if (args.PropertyName == "IsChecked")
// Invoke Your VM Function Here
}
That should be it.
In Xaml:
add the following namspace..
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Now for you checkbox add the following code:
<CheckBox>
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding CheckedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
In ViewModel:
public ICommand CheckedCommand
{
get
{
return new DelegateCommand(OnChecked);//Delegate command is the Implemntation of Icommand Interface
}
}
public void OnLogin(object param)
{
//code for you checked event
}
Hope this will help you.
I want to use the DataGridComboBoxColumn as a autocomplete combobox.
I've got it partially working. When the Row is in EditMode I can type text in the ComboBox, also in ViewMode the control returns the text. Only how to get the Label (in template) to EditMode by mouse doubleclick?
Up front, I don't want to use the DataGridTemplateColumn control because it just doesn't handle keyboard and mouse entry like the DataGridComboBoxColumn does (tabs, arrows, edit/view mode/ double click etc..).
It looks like:
I fixed it adding a behavior to the TextBox to get a link to the parent DataGrid then setting the Row into Edit Mode by calling BeginEdit().
The solution I used:
View
<Window x:Class="WpfApplication1.MainWindow"
xmlns:local="clr-namespace:WpfApplication1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Window.Resources>
<local:BindingProxy x:Key="proxy" Data="{Binding}" />
</Window.Resources>
<Grid>
<DataGrid ItemsSource="{Binding Model.Things}" Name="MyGrid" ClipboardCopyMode="IncludeHeader">
<DataGrid.Resources>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridComboBoxColumn Header="Object" MinWidth="140" TextBinding="{Binding ObjectText}" ItemsSource="{Binding Source={StaticResource proxy}, Path=Data.Model.ObjectList}" >
<DataGridComboBoxColumn.EditingElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsEditable" Value="True"/>
<Setter Property="Text" Value="{Binding ObjectText}"/>
<Setter Property="IsSynchronizedWithCurrentItem" Value="True" />
</Style>
</DataGridComboBoxColumn.EditingElementStyle>
<DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<TextBox IsReadOnly="True" Text="{Binding Path=DataContext.ObjectText, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type DataGridRow}}}">
<TextBox.Resources>
<Style TargetType="{x:Type TextBox}">
<Setter Property="local:CellSelectedBehavior.IsCellRowSelected" Value="true"></Setter>
</Style>
</TextBox.Resources>
</TextBox>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DataGridComboBoxColumn.ElementStyle>
</DataGridComboBoxColumn>
</DataGrid.Columns>
</DataGrid>
</Grid>
</Window>
Model
public class Model : BaseModel
{
//List of objects for combobox
private List<string> _objectList;
public List<string> ObjectList { get { return _objectList; } set { _objectList = value; } }
//Rows in datagrid
private List<Thing> _things;
public List<Thing> Things
{
get { return _things; }
set { _things = value; OnPropertyChanged("Things"); }
}
}
public class Thing : BaseModel
{
//Text in combobox
private string _objectText;
public string ObjectText
{
get { return _objectText; }
set { _objectText = value; OnPropertyChanged("ObjectText"); }
}
}
ViewModel
public class ViewModel
{
public Model Model { get; set; }
public ViewModel()
{
Model = new WpfApplication1.Model();
Model.ObjectList = new List<string>();
Model.ObjectList.Add("Aaaaa");
Model.ObjectList.Add("Bbbbb");
Model.ObjectList.Add("Ccccc");
Model.Things = new List<Thing>();
Model.Things.Add(new Thing() { ObjectText = "Aaaaa" });
}
}
Behavior
public class CellSelectedBehavior
{
public static bool GetIsCellRowSelected(DependencyObject obj) { return (bool)obj.GetValue(IsCellRowSelectedProperty); }
public static void SetIsCellRowSelected(DependencyObject obj, bool value) { obj.SetValue(IsCellRowSelectedProperty, value); }
public static readonly DependencyProperty IsCellRowSelectedProperty = DependencyProperty.RegisterAttached("IsCellRowSelected",
typeof(bool), typeof(CellSelectedBehavior), new UIPropertyMetadata(false, OnIsCellRowSelected));
static void OnIsCellRowSelected(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
{
TextBox item = depObj as TextBox;
if (item == null)
return;
if (e.NewValue is bool == false)
return;
if ((bool)e.NewValue)
item.MouseDoubleClick += SelectRow;
else
item.MouseDoubleClick -= SelectRow;
}
static void SelectRow(object sender, EventArgs e)
{
TextBox box = sender as TextBox;
var grid = box.FindAncestor<DataGrid>();
grid.BeginEdit();
}
}
Helper (to find DataGrid)
public static class Helper
{
public static T FindAncestor<T>(this DependencyObject current) where T : DependencyObject
{
current = VisualTreeHelper.GetParent(current);
while (current != null)
{
if (current is T)
{
return (T)current;
}
current = VisualTreeHelper.GetParent(current);
};
return null;
}
}
I'm using the EventToCommand Class from the MVVM-light-Toolkit to handle the AutoGeneratingColumn-Event in the WPF-DataGrid. It works fine in my Main-DataGrid, but I use another DataGrid in the RowDetailsTemplate and here I got a problem:
The AutoGeneratingColumn fires before the EventToCommand-Object was generated. Is there a solution for this problem?
Here is a piece of my Xaml-Code:
<DataGrid DockPanel.Dock="Top" AutoGenerateColumns="True" Name="table" VerticalAlignment="Top" ItemsSource="{Binding PartBatchList}" IsReadOnly="True">
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn">
<hgc:EventToCommand Command="{Binding AutoGeneratingColumnCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
<DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel Margin="30,0,30,30" Orientation="Vertical">
<Border CornerRadius="4" Padding="5" Background="White">
<DataGrid ItemsSource="{Binding Workpieces}"
CanUserReorderColumns="False" CanUserResizeColumns="False" CanUserSortColumns="False"
AutoGenerateColumns="True" AutoGeneratingColumn="WorkpieceListAutoGeneratingColumn">
<i:Interaction.Triggers>
<i:EventTrigger EventName="AutoGeneratingColumn">
<hgc:EventToCommand Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type DataGrid},AncestorLevel=2}, Path=DataContext.AutoGeneratingColumnCommand}" PassEventArgsToCommand="True"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</DataGrid>
</Border>
</StackPanel>
</DataTemplate>
</DataGrid.RowDetailsTemplate>
</DataGrid>
The Event-Handler WorkpieceListAutoGeneratingColumn in the Code-Behind File is called, the Command in my ViewModel is never called.
Andreas
The reason should be that you can't have an event andler and an event to command on the same object/event combination. Remove the AutoGeneratingColumn="WorkpieceListAutoGeneratingColumn" from your DataGrid an the command should be called.
Had the problem once myself :-)
Edit
If you need the eventhandler in the code behind, remove the EventToCommand and call the command in your code behind, e.g.
public void WorkpieceListAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs args) {
var vm = ((YourViewModel) this.DataContext);
if (vm.AutoGeneratingColumnCommand.CanExecute(eventArgs))
vm.AutoGeneratingColumnCommand.Execute(eventArgs);
}
But, I think that the first option is the better one.
Edit 2
OK, had some look around and it seems that <i:Interaction.Triggers/> only works after the object is already rendered and user interaction takes place (hence the name?). Well, this means that there are simply just some events - the ones that are called during the construction of the object - that cannot be handled by the EventToCommand mechanism. In these cases it is OK to use code behind to call your command from there, see my first edit.
You don't have to use evil code behind ;-) You can do this using an attached behaviour...
public class AutoGeneratingColumnEventToCommandBehaviour
{
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached(
"Command",
typeof(ICommand),
typeof(AutoGeneratingColumnEventToCommandBehaviour),
new PropertyMetadata(
null,
CommandPropertyChanged));
public static void SetCommand(DependencyObject o, ICommand value)
{
o.SetValue(CommandProperty, value);
}
public static ICommand GetCommand(DependencyObject o)
{
return o.GetValue(CommandProperty) as ICommand;
}
private static void CommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var dataGrid = d as DataGrid;
if (dataGrid != null)
{
if (e.OldValue != null)
{
dataGrid.AutoGeneratingColumn -= OnAutoGeneratingColumn;
}
if (e.NewValue != null)
{
dataGrid.AutoGeneratingColumn += OnAutoGeneratingColumn;
}
}
}
private static void OnAutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
var dependencyObject = sender as DependencyObject;
if (dependencyObject != null)
{
var command = dependencyObject.GetValue(CommandProperty) as ICommand;
if (command != null && command.CanExecute(e))
{
command.Execute(e);
}
}
}
}
Then use it in XAML like this...
<DataGrid ItemsSource="{Binding MyGridSource}"
AttachedCommand:AutoGeneratingColumnEventToCommandBehaviour.Command="{Binding CreateColumnsCommand}">
</DataGrid>