WPF Drag n drop doesn't fire CommanBinding.CanExecute - wpf

Yes, I know it sounds weird, but it doesn't, the question is why, and if there's a work around. It works with everything, even when you hit PrintScreen or Pause keys, CanExecute fires. So after doing a drag drop, in order to make it fire, you have to do "something" else, like a mouse click, focus, hit a key, anything. That'll make the event fire, and allow Execute to happen. Anyway, here's my code, I know it's long, but it'll help you help me.
I found this bug in our large main project, so I simplified it to this little app to isolate the problem.
XAML:
<Window x:Class="DragNDropCommands.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="485" SizeToContent="Width" Loaded="Window_Loaded">
<Window.CommandBindings>
<CommandBinding Command="ApplicationCommands.New" CanExecute="NewCanExecute" Executed="NewExecuted" />
<CommandBinding Command="ApplicationCommands.Save" CanExecute="SaveCanExecute" Executed="SaveExecuted" />
<CommandBinding Command="ApplicationCommands.Undo" CanExecute="UndoCanExecute" Executed="UndoExecuted" />
<CommandBinding Command="ApplicationCommands.Redo" CanExecute="RedoCanExecute" Executed="RedoExecuted" />
</Window.CommandBindings>
<Grid Margin="8">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Button Command="ApplicationCommands.New" Grid.Row="0" Grid.Column="0" FontWeight="Bold" Content="New" Width="80" Margin="8"></Button>
<Button Command="ApplicationCommands.Save" Grid.Row="0" Grid.Column="1" FontWeight="Bold" Content="Save" Width="80" Margin="8"></Button>
<Button Command="ApplicationCommands.Undo" Grid.Row="0" Grid.Column="2" FontWeight="Bold" Content="Undo" Width="80" Margin="8"></Button>
<Button Command="ApplicationCommands.Redo" Grid.Row="0" Grid.Column="3" FontWeight="Bold" Content="Redo" Width="80" Margin="8"></Button>
<CheckBox Grid.Row="1" Grid.Column="0" Margin="8" IsChecked="{Binding Path=AllowNew, Mode=TwoWay}">Allow New</CheckBox>
<CheckBox Grid.Row="1" Grid.Column="1" Margin="8" IsChecked="{Binding Path=AllowSave}">Allow Save</CheckBox>
<CheckBox Grid.Row="1" Grid.Column="2" Margin="8" IsChecked="{Binding Path=AllowUndo}">Allow Undo</CheckBox>
<CheckBox Grid.Row="1" Grid.Column="3" Margin="8" IsChecked="{Binding Path=AllowRedo}">Allow Redo</CheckBox>
<Label x:Name="labelDrag" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="2" BorderBrush="Black" BorderThickness="1" MouseDown="Label_MouseDown"
Background="LightBlue" HorizontalContentAlignment="Center" Margin="8">Drag this label...</Label>
<Label x:Name="labelDropNew" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" BorderBrush="Black" BorderThickness="1" Drop="labelDropNew_Drop"
Background="LightGray" HorizontalContentAlignment="Center" Margin="8" AllowDrop="True">...here to toggle AllowNew</Label>
<Label x:Name="labelDropSave" Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="2" BorderBrush="Black" BorderThickness="1" Drop="labelDropSave_Drop"
Background="LightGray" HorizontalContentAlignment="Center" Margin="8" AllowDrop="True">...here to toggle AllowSave</Label>
<ListBox x:Name="listViewLog" Grid.Row="4" Grid.ColumnSpan="4" Margin="8" Width="500">
</ListBox>
<Button Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="2" Margin="8" Click="Button_Click">Clear list</Button>
</Grid>
</Window>
C#:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
namespace DragNDropCommands
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
private CommandControl commandControl = new CommandControl();
private int canExecuteCount = 1;
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.DataContext = commandControl;
}
private void NewCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (this.commandControl == null || listViewLog == null)
return;
e.CanExecute = this.commandControl.AllowNew;
listViewLog.Items.Add
(
String.Format
(
"{0} - NewCanExecute: {1} - commandControl.AllowNew: {2}",
canExecuteCount++, e.CanExecute, commandControl.AllowNew
)
);
}
private void NewExecuted(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("New executed");
}
private void SaveCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (this.commandControl == null || listViewLog == null)
return;
e.CanExecute = this.commandControl.AllowSave;
listViewLog.Items.Add
(
String.Format
(
"{0} - SaveCanExecute: {1} - commandControl.AllowSave: {2}",
canExecuteCount++, e.CanExecute, commandControl.AllowSave
)
);
}
private void SaveExecuted(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Save executed");
}
private void UndoCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (this.commandControl == null || listViewLog == null)
return;
e.CanExecute = this.commandControl.AllowUndo;
listViewLog.Items.Add
(
String.Format
(
"{0} - UndoCanExecute: {1} - commandControl.AllowUndo: {2}",
canExecuteCount++, e.CanExecute, commandControl.AllowUndo
)
);
}
private void UndoExecuted(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Undo executed");
}
private void RedoCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (this.commandControl == null || listViewLog == null)
return;
e.CanExecute = this.commandControl.AllowRedo;
listViewLog.Items.Add
(
String.Format
(
"{0} - RedoCanExecute: {1} - commandControl.AllowRedo: {2}",
canExecuteCount++, e.CanExecute, commandControl.AllowRedo
)
);
}
private void RedoExecuted(object sender, ExecutedRoutedEventArgs e)
{
MessageBox.Show("Redo executed");
}
private void Button_Click(object sender, RoutedEventArgs e)
{
listViewLog.Items.Clear();
}
private void Label_MouseDown(object sender, MouseButtonEventArgs e)
{
Label label = (Label)sender;
if(e.LeftButton == MouseButtonState.Pressed)
DragDrop.DoDragDrop(label, label, DragDropEffects.Move);
}
private void labelDropNew_Drop(object sender, DragEventArgs e)
{
this.commandControl.AllowNew = !this.commandControl.AllowNew;
}
private void labelDropSave_Drop(object sender, DragEventArgs e)
{
this.commandControl.AllowSave = !this.commandControl.AllowSave;
}
}
public class CommandControl : DependencyObject
{
public bool AllowNew
{
get { return (bool)GetValue(AllowNewProperty); }
set { SetValue(AllowNewProperty, value); }
}
// Using a DependencyProperty as the backing store for AllowNew. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllowNewProperty =
DependencyProperty.Register("AllowNew", typeof(bool), typeof(CommandControl), new UIPropertyMetadata(false));
public bool AllowSave
{
get { return (bool)GetValue(AllowSaveProperty); }
set { SetValue(AllowSaveProperty, value); }
}
// Using a DependencyProperty as the backing store for AllowSave. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllowSaveProperty =
DependencyProperty.Register("AllowSave", typeof(bool), typeof(CommandControl), new UIPropertyMetadata(false));
public bool AllowUndo
{
get { return (bool)GetValue(AllowUndoProperty); }
set { SetValue(AllowUndoProperty, value); }
}
// Using a DependencyProperty as the backing store for AllowUndo. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllowUndoProperty =
DependencyProperty.Register("AllowUndo", typeof(bool), typeof(CommandControl), new UIPropertyMetadata(false));
public bool AllowRedo
{
get { return (bool)GetValue(AllowRedoProperty); }
set { SetValue(AllowRedoProperty, value); }
}
// Using a DependencyProperty as the backing store for AllowRedo. This enables animation, styling, binding, etc...
public static readonly DependencyProperty AllowRedoProperty =
DependencyProperty.Register("AllowRedo", typeof(bool), typeof(CommandControl), new UIPropertyMetadata(false));
}
}
You should be able to just copy paste and do a few name changes (files, namespaces) to get it running. I'd really love your help since this has been driving me nuts, and now that I finally discover the reason for the bug, I don't know what to do about it.
Any suggestion is really apreciatted.
Thanks in advance.

Just a quick suggestion:
You could use CommandManager.InvalidateRequerySuggested() in the drag and drop event handler to force the reexecution of CanExecute(). (Link)

Related

Proportional sizing in wpf

I have a Grid having two columns. One column is twice the width as other.
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="12"/>
<TextBox Grid.Column="1" Text="" />
</Grid>
But during runtime when i type in second text box the width changes without maintaining the 1:2 ratio.
I dont want to keep fixed width. The width will be changed at runtime based on user entry.
I want the width to be in 1:2 ratio even if the width changes at runtime?
Here is another, alternative answer that you can modify into your needs.
In this example, you can enter the width of your grid in the textbox in the first column. Or you can expand or decrease the width with the button. Just for illustration. You may have to change this for your purpose.
MainWindow.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public static readonly DependencyProperty GridWidthProperty = DependencyProperty.Register("GridWidth", typeof(Double), typeof(MainWindow), new UIPropertyMetadata(300d, OnGridWidthPropertyChanged));
public Double GridWidth
{
get { return (Double)GetValue(GridWidthProperty); }
set
{
SetValue(GridWidthProperty, value);
NotifyPropertyChanged("GridWidth");
}
}
public static readonly DependencyProperty ColumnWidthProperty = DependencyProperty.Register("ColumnWidth", typeof(String), typeof(MainWindow), new UIPropertyMetadata("100", OnColumnWidthPropertyChanged));
public String ColumnWidth
{
get { return (String)GetValue(ColumnWidthProperty); }
set
{
SetValue(ColumnWidthProperty, value);
NotifyPropertyChanged("ColumnWidth");
}
}
private static void OnGridWidthPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
MainWindow ctl = sender as MainWindow;
ctl.doGridWidthChanged();
ctl = null;
}
private static void OnColumnWidthPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
MainWindow ctl = sender as MainWindow;
ctl.doColumnWidthChanged();
ctl = null;
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void button_Click(object sender, RoutedEventArgs e)
{
if (sender == button1)
this.GridWidth += 50;
else if (sender == button2)
this.GridWidth -= 50;
}
private void doGridWidthChanged()
{
if (Double.IsNaN(this.GridWidth))
return;
this.ColumnWidth = Math.Round((this.GridWidth / 3), 2).ToString();
}
private void doColumnWidthChanged()
{
Double columnwidthval = Double.NaN;
if (!String.IsNullOrEmpty(this.ColumnWidth) && Double.TryParse(this.ColumnWidth, out columnwidthval))
this.GridWidth = columnwidthval * 3;
else
this.ColumnWidth = Math.Round((this.GridWidth / 3), 2).ToString();
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}
And here is my XAML code.
MainWindow.xaml
<Window x:Class="WpfApplication3.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication3"
Title="MainWindow" Height="600" Width="800">
<Grid>
<Grid Margin="0,60,0,0"
Width="{Binding Path=GridWidth}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Background="GhostWhite" />
<Border Grid.Column="1" Background="AliceBlue" />
<Border Grid.ColumnSpan="2" BorderBrush="DimGray" BorderThickness="1" />
<StackPanel Grid.Column="0" Orientation="Vertical" Margin="3">
<TextBlock Text="Single" />
<TextBox Text="{Binding Path=ColumnWidth, Mode=TwoWay}" />
</StackPanel>
<StackPanel Grid.Column="1" Orientation="Vertical" Margin="3">
<TextBlock Text="Double" />
</StackPanel>
</Grid>
<Button Content="Increase" Height="34" HorizontalAlignment="Left" Margin="19,12,0,0" Name="button1" VerticalAlignment="Top" Width="90" Click="button_Click" />
<Button Content="Decrease" Height="34" HorizontalAlignment="Left" Margin="120,12,0,0" Name="button2" VerticalAlignment="Top" Width="90" Click="button_Click" />
</Grid>
</Window>
Hope that helps!
This can be done like this:
MainWindow.xaml
<Grid>
<Grid Width="{Binding Path=GridWidth}"
Margin="0,60,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*" />
<ColumnDefinition Width="2*" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Text="Single 33%"/>
<TextBox Grid.Column="1" Text="Double 67%" />
</Grid>
<Button Content="Increase" Height="34" HorizontalAlignment="Left" Margin="19,12,0,0" Name="button1" VerticalAlignment="Top" Width="90" Click="button_Click" />
<Button Content="Decrease" Height="34" HorizontalAlignment="Left" Margin="120,12,0,0" Name="button2" VerticalAlignment="Top" Width="90" Click="button_Click" />
</Grid>
I have two buttons that increase or decrease the total grid width for illustration.
MainWindows.cs
public partial class MainWindow : Window, INotifyPropertyChanged
{
public static readonly DependencyProperty GridWidthProperty = DependencyProperty.Register("GridWidth", typeof(Double), typeof(MainWindow), new UIPropertyMetadata(300d));
public Double GridWidth
{
get { return (Double)GetValue(GridWidthProperty); }
set
{
SetValue(GridWidthProperty, value);
NotifyPropertyChanged("GridWidth");
}
}
public MainWindow()
{
InitializeComponent();
this.DataContext = this;
}
private void button_Click(object sender, RoutedEventArgs e)
{
if (sender == button1)
this.GridWidth += 50;
else if (sender == button2)
this.GridWidth -= 50;
}
public event PropertyChangedEventHandler PropertyChanged;
public void NotifyPropertyChanged(String PropertyName)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(PropertyName));
}
}

DataGrid - Mouse enter + leave row events

I would like to subscribe to the mouse enter and leave events of a DataGridRow.
My XAML for the DataGrid looks like this at the moment:
<toolkit:DataGrid ItemsSource="{Binding DisplayedSearchResults}"
caliburn:Message.Attach="[Event MouseDoubleClick] = [OpenDocument()]"
SelectedItem="{Binding SelectedRow, Mode=TwoWay}" Margin="7"
AutoGeneratingColumn="DataGrid_AutoGeneratingColumn"
IsReadOnly="True" Grid.Row="0"
Sorting="ResultsDataGrid_Sort">
<toolkit:DataGrid.Resources>
<Style TargetType="Button"></Style>
</toolkit:DataGrid.Resources>
</toolkit:DataGrid>
How can I do this using Caliburn if possible, but code-behind if not?
I ended hooking up in the code-behind:
MyView.xaml.cs:
public partial class MyView : UserControl
{
public SearchResultsView()
{
InitializeComponent();
SearchResultsGrid.LoadingRow += DataGrid_PreparingRow;
}
public void DataGrid_PreparingRow(object sender, DataGridRowEventArgs args)
{
args.Row.MouseEnter += Row_MouseEnter;
args.Row.MouseLeave += Row_MouseLeave;
}
public void Row_MouseEnter(object sender, MouseEventArgs args)
{
// do some stuff
}
public void Row_MouseLeave(object sender, MouseEventArgs args)
{
// do some stuff
}
}
MyView.xaml:
<UserControl x:Class="MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:toolkit="clr-namespace:Microsoft.Windows.Controls;assembly=WPFToolkit">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<toolkit:DataGrid Name="SearchResultsGrid" />
</Grid>
</UserControl>

Dynamic template wpf

i have made a template that look like this :
<ControlTemplate x:Key="onoffValue" TargetType="{x:Type Control}">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" Height="20" Margin="0,5,0,0">
<RadioButton Content="On" Height="20" Name="On_radiobutton" />
<RadioButton Content="Off" Height="20" Name="Off_radiobutton" Margin="20,0,0,0" />
</StackPanel>
<ControlTemplate.Triggers>
<DataTrigger Binding="{Binding Path=BootSector}" Value="true">
<Setter TargetName="On_radiobutton" Property="IsChecked" Value="true"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=BootSector}" Value="false">
<Setter TargetName="Off_radiobutton" Property="IsChecked" Value="true"/>
</DataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
For now, it is bind to the property BootSector(bool) ofa "Configuration" object.
I use this template in my window that has a configuration object as data context like this :
<Control Template="{StaticResource onoffValue}">
</Control>
It works great, but i want to go further.
I would like to know how i can pass a different property to my template to dynamically bind (dynamically change the property the template is bind to)
ie i tryed something like
<Control Template="{StaticResource onoffValue}" xmlns:test="{Binding Path=BootSector}"/>
and bind it in the template to "test" but it doesn't work
Is it possible ? How can i do that ? I think i'm not too far away but not there still !
Thank you in advance
Edit : Concerning Dmitry answer :
There is a bug using that. When i do :
<StackPanel local:ToggleControl.IsOn="{Binding BootSector, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Grid.Row="0" Grid.Column="1"
Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">
<RadioButton Content="On" local:ToggleControl.Role="On" Height="20" Margin="5" />
<RadioButton Content="Off" local:ToggleControl.Role="Off" Height="20" Margin="5" />
</StackPanel>
By default BootSector is on false. When i click on the on button (true), it sets bootSector to true and then immediately to false . The behaviour should be that it stays to true until it is unchecked ? Is this related to the problem related here ? http://geekswithblogs.net/claraoscura/archive/2008/10/17/125901.aspx
Here, the idea is - generic behaviors are never complex and generally not worth creating a custom control. I undertand that implmentation may vary, but the approach will remain the same. It makes sense to use XAML for the parts which can change and code for the stuff which will remain constant.
UPDATE 1- It's getting even easier when using Custom controls. You won't need attached property no more - as you'll get a dedicated space for it inside your custom control, also, you can use x:Name and GetTemplateChild(..) to otain a reference to individual RadioButtons.
Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace RadioButtons
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.Loaded += (o, e) =>
{
this.DataContext = new TwoBoolean()
{
PropertyA = false,
PropertyB = true
};
};
}
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(((TwoBoolean)this.DataContext).ToString());
}
}
public enum RadioButtonRole
{
On,
Off
}
public class ToggleControl : DependencyObject
{
public static readonly DependencyProperty IsOnProperty =
DependencyProperty.RegisterAttached("IsOn",
typeof(bool?),
typeof(ToggleControl),
new PropertyMetadata(null,
new PropertyChangedCallback((o, e) =>
{
ToggleControl.OnIsOnChanged((Panel)o, (bool)e.NewValue);
})));
public static readonly DependencyProperty RoleProperty =
DependencyProperty.RegisterAttached("Role",
typeof(RadioButtonRole?),
typeof(ToggleControl),
new PropertyMetadata(null,
new PropertyChangedCallback((o, e) =>
{
})));
private static readonly DependencyProperty IsSetUpProperty =
DependencyProperty.RegisterAttached("IsSetUp",
typeof(bool),
typeof(ToggleControl),
new PropertyMetadata(false));
private static void OnIsOnChanged(Panel panel, bool e)
{
if (!ToggleControl.IsSetup(panel))
{
ToggleControl.Setup(panel);
}
RadioButtonRole role;
if (e)
{
role = RadioButtonRole.On;
}
else
{
role = RadioButtonRole.Off;
}
ToggleControl.GetRadioButtonByRole(role, panel).IsChecked = true;
}
private static void Setup(Panel panel)
{
// get buttons
foreach (RadioButton radioButton in
new RadioButtonRole[2]
{
RadioButtonRole.On,
RadioButtonRole.Off
}.Select(t =>
ToggleControl.GetRadioButtonByRole(t, panel)))
{
radioButton.Checked += (o2, e2) =>
{
RadioButton checkedRadioButton = (RadioButton)o2;
panel.SetValue(ToggleControl.IsOnProperty,
ToggleControl.GetRadioButtonRole(checkedRadioButton) == RadioButtonRole.On);
};
}
panel.SetValue(ToggleControl.IsSetUpProperty, true);
}
private static bool IsSetup(Panel o)
{
return (bool)o.GetValue(ToggleControl.IsSetUpProperty);
}
private static RadioButton GetRadioButtonByRole(RadioButtonRole role,
Panel container)
{
return container.Children.OfType<RadioButton>().First(t =>
(RadioButtonRole)t.GetValue(ToggleControl.RoleProperty) == role);
}
private static RadioButtonRole GetRadioButtonRole(RadioButton radioButton)
{
return (RadioButtonRole)radioButton.GetValue(ToggleControl.RoleProperty);
}
public static void SetIsOn(DependencyObject o, bool? e)
{
o.SetValue(ToggleControl.IsOnProperty, e);
}
public static bool? GetIsOn(DependencyObject e)
{
return (bool?)e.GetValue(ToggleControl.IsOnProperty);
}
public static void SetRole(DependencyObject o, RadioButtonRole? e)
{
o.SetValue(ToggleControl.RoleProperty, e);
}
public static RadioButtonRole? GetRole(DependencyObject e)
{
return (RadioButtonRole?)e.GetValue(ToggleControl.RoleProperty);
}
}
public class TwoBoolean: INotifyPropertyChanged
{
private bool propertyA, propertyB;
public bool PropertyA
{
get
{
return this.propertyA;
}
set
{
this.propertyA = value;
this.OnPropertyChanged("PropertyA");
}
}
public bool PropertyB
{
get
{
return this.propertyB;
}
set
{
this.propertyB = value;
this.OnPropertyChanged("PropertyB");
}
}
private void OnPropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
{
this.PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
}
public override string ToString()
{
return string.Format("PropertyA:{0}, PropertyB:{1}", this.PropertyA, this.PropertyB);
}
public event PropertyChangedEventHandler PropertyChanged;
}
}
Markup:
<Window x:Class="RadioButtons.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RadioButtons"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Grid.Row="0" Grid.Column="0" Margin="5" VerticalAlignment="Center">PropertyA</TextBlock>
<StackPanel local:ToggleControl.IsOn="{Binding PropertyA, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Grid.Row="0" Grid.Column="1"
Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">
<RadioButton Content="On" local:ToggleControl.Role="On" Height="20" Margin="5" />
<RadioButton Content="Off" local:ToggleControl.Role="Off" Height="20" Margin="5" />
</StackPanel>
<TextBlock Grid.Row="1" Grid.Column="0" Margin="5" VerticalAlignment="Center">PropertyB</TextBlock>
<StackPanel local:ToggleControl.IsOn="{Binding PropertyB, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"
Grid.Row="1" Grid.Column="1"
Orientation="Horizontal" HorizontalAlignment="Center" Margin="5">
<RadioButton Content="On" local:ToggleControl.Role="On" Height="20" Margin="5" />
<RadioButton Content="Off" local:ToggleControl.Role="Off" Height="20" Margin="5" />
</StackPanel>
<Button Click="Button_Click" Grid.Row="3" Grid.ColumnSpan="2">Save</Button>
</Grid>
</Window>
You should not use an xmlns to pass a parameter, rather use the Tag or template a ContentControl, then you can bind the Content to your property (set it to TwoWay) and use a TemplateBinding to Content inside the template.

Focus on a WPF TextBox doesn't work properly with attached properties

In WPF MVVM environment, I'm trying to ensure that focus is given to a TextBox. What happens is that the Cursor appears in the TextBox but is not flashing and the TextBox does not have focus. The code is:
I have a Popup containing this UserControl:
<UserControl x:Class="Rendevous.BusinessModules.AdministrationModule.LogonView"
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"
xmlns:my="clr-namespace:Csla.Xaml;assembly=Csla.Xaml"
xmlns:ViewModels="clr-namespace:Rendevous.Common.ViewModels;assembly=Common"
Visibility="{Binding Path=IsViewVisible}"
FocusManager.FocusedElement="{Binding Path=passCodeTextBox}"
ViewModels:FocusExtension.IsFocused="{Binding IsPassCodeFocused}">
<Grid >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<Label Grid.Column="0" Content="Pass Code:"
VerticalAlignment="Center" />
<TextBox Grid.Column="1" Name="passCodeTextBox" Width="100"
VerticalAlignment="Center"
Margin="3"
Text="{Binding Path=PassCode, UpdateSourceTrigger=PropertyChanged}"
ViewModels:FocusExtension.IsFocused="{Binding IsPassCodeFocused}" />
<Button Grid.Column="2" VerticalAlignment="Center" Margin="3" Name="loginButton"
Content="Log In" />
<Button Grid.Column="3"
VerticalAlignment="Center"
Margin="3"
Name="cancelButton"
Content="Cancel" />
</Grid>
(I've removed some Button handling stuff!)
In my ViewModel, I have code like:
public void LogonButtonClicked(object sender, ExecuteEventArgs e)
{
if (securityService.Login(PassCode))
{
eventBroker.Invoke(EventName.CloseLogonView, this);
}
else
{
IsViewVisible = Visibility.Hidden;
msgService.ShowError("Pass Code was not recognised", "Logon Error");
IsViewVisible = Visibility.Visible;
PassCode = "";
IsPassCodeFocused = true;
}
}
I am using an attached property:
public class FocusExtension
{
public static readonly DependencyProperty IsFocusedProperty = DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(FocusExtension), new FrameworkPropertyMetadata(IsFocusedChanged));
public static bool? GetIsFocused(DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool?)element.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject element, bool? value)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
element.SetValue(IsFocusedProperty, value);
}
private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
FrameworkElement fe = (FrameworkElement)d;
if (e.OldValue == null)
{
fe.GotFocus += FrameworkElement_GotFocus;
fe.LostFocus += FrameworkElement_LostFocus;
}
if ((bool)e.NewValue)
{
fe.Focus();
}
}
private static void FrameworkElement_GotFocus(object sender, RoutedEventArgs e)
{
((FrameworkElement)sender).SetValue(IsFocusedProperty, true);
}
private static void FrameworkElement_LostFocus(object sender, RoutedEventArgs e)
{
((FrameworkElement)sender).SetValue(IsFocusedProperty, false);
}
}
}
What happens is that the cursor appears in the TextBox but is not flashing. The TextBox does not have focus because nothing appears when you type. If you click on it, it works fine.
What have I done wrong?
I couldn't reproduce it using the code you provided, but two things I noticed are:
1) On LogonView, I think your intent was
FocusManager.FocusedElement="{Binding ElementName=passCodeTextBox}"
and not
FocusManager.FocusedElement="{Binding Path=passCodeTextBox}"
2) It looks like IsFocused is applied in multiple places. I'd try setting a breakpoint in IsFocusedChanged() and see which control gets it last.
Between that, and watching FocusManager.FocusedElement ( http://msdn.microsoft.com/en-us/library/system.windows.input.focusmanager.focusedelement.aspx ) and Keyboard.FocusedElement ( http://msdn.microsoft.com/en-us/library/system.windows.input.keyboard.focusedelement ) you should be able to track down where focus is really going.

Is there a color dialog for WPF?

I am looking for a color dialog for WPF?
Is there one built in?
Should I build my own ?
Or do Win 32 interop?
If so, how?
I wrote a simple WPF Color picker which supports the following features a while back
3 different types of color swatch
Opacity slider
Mouse mover control for selecting color
Sets to current color on open
Standard dialog buttons
Here is the article in case you need it : http://www.codeproject.com/Articles/33001/WPF-A-Simple-Color-Picker-With-Preview
Heres a simple color picker using attached properties, should anyone be looking for example code
public static class BrushExtender
{
public readonly static DependencyProperty BrushProperty = DependencyProperty.RegisterAttached("Brush", typeof(Brush), typeof(BrushExtender), new PropertyMetadata(Brushes.Black,DoBrushChanged));
public readonly static DependencyProperty RedChannelProperty = DependencyProperty.RegisterAttached("RedChannel", typeof(int), typeof(BrushExtender), new PropertyMetadata(DoColorChangedRed));
public readonly static DependencyProperty GreenChannelProperty = DependencyProperty.RegisterAttached("GreenChannel", typeof(int), typeof(BrushExtender), new PropertyMetadata(DoColorChangedGreen));
public readonly static DependencyProperty BlueChannelProperty = DependencyProperty.RegisterAttached("BlueChannel", typeof(int), typeof(BrushExtender), new PropertyMetadata(DoColorChangedBlue));
public readonly static DependencyProperty AlphaChannelProperty = DependencyProperty.RegisterAttached("AlphaChannel", typeof(int), typeof(BrushExtender), new PropertyMetadata(DoColorChangedAlpha));
public readonly static DependencyProperty ColourValueProperty = DependencyProperty.RegisterAttached("ColourValue", typeof(string), typeof(BrushExtender), new PropertyMetadata(DoValueChanged));
public static void SetRedChannel(DependencyObject o, int value)
{
o.SetValue(RedChannelProperty, value);
}
public static void SetGreenChannel(DependencyObject o, int value)
{
o.SetValue(GreenChannelProperty, value);
}
public static void SetBlueChannel(DependencyObject o, int value)
{
o.SetValue(BlueChannelProperty, value);
}
public static void SetAlphaChannel(DependencyObject o, int value)
{
o.SetValue(AlphaChannelProperty, value);
}
public static void SetBrush(DependencyObject o, SolidColorBrush brush)
{
o.SetValue(BrushProperty, brush);
}
public static void SetColourValue(DependencyObject o, string value)
{
o.SetValue(ColourValueProperty, value);
}
private static void DoColorChangedRed(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
DoColorChange(d, (int)e.NewValue, c => c.R, () => Color.FromArgb(color.A, ((byte)(int)e.NewValue), color.G , color.B));
}
private static void DoColorChangedGreen(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
DoColorChange(d, (int)e.NewValue, c => c.G, () => Color.FromArgb(color.A, color.R, ((byte)(int)e.NewValue), color.B));
}
private static void DoColorChangedBlue(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
DoColorChange(d, (int)e.NewValue, c => c.B, () => Color.FromArgb(color.A, color.R, color.G, (byte)(int)e.NewValue));
}
private static void DoColorChangedAlpha(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
DoColorChange(d, (int)e.NewValue, c => c.A, () => Color.FromArgb((byte)(int)e.NewValue, color.R, color.G, color.B));
}
private static void DoColorChange(DependencyObject d, int newValue, Func<Color, int> colorCompare, Func<Color> getColor)
{
var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
if (colorCompare(color) == newValue)
return;
var newBrush = new SolidColorBrush(getColor());
d.SetValue(BrushProperty, newBrush);
d.SetValue(ColourValueProperty, newBrush.Color.ToString());
}
private static void DoValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var color = ((SolidColorBrush)d.GetValue(BrushProperty)).Color;
if (color.ToString() == (string)e.NewValue)
return;
Color? newColour = null;
try
{
newColour = (Color) ColorConverter.ConvertFromString((string) e.NewValue);
}
catch{}
if (newColour == null)
return;
var newBrush = new SolidColorBrush(newColour.Value);
d.SetValue(BrushProperty, newBrush);
}
private static void DoBrushChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue == e.OldValue)
return;
var colour = ((SolidColorBrush)e.NewValue).Color;
d.SetValue(RedChannelProperty,(int)colour.R);
d.SetValue(GreenChannelProperty,(int)colour.G);
d.SetValue(BlueChannelProperty,(int)colour.B);
d.SetValue(AlphaChannelProperty,(int)colour.A);
d.SetValue(ColourValueProperty,colour.ToString());
}
}
and here it is being used
<Window x:Class="ChannelColourBrush.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:c="clr-namespace:ChannelColourBrush" Title="Window1" Height="300" Width="300">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<TextBlock Text="Red" />
<TextBlock Text="Green" Grid.Row="1" />
<TextBlock Text="Blue" Grid.Row="2" />
<TextBlock Text="Alpha" Grid.Row="3" />
<Slider Name="redSlider" Grid.Column="1" Minimum="0" Maximum="255" Width="200" Height="20" Grid.ColumnSpan="2" Value="{Binding ElementName=rect, Path=(c:BrushExtender.RedChannel), Mode=TwoWay}" />
<Slider Name="greenSlider" Grid.Column="1" Grid.Row="1" Minimum="0" Maximum="255" Width="200" Height="20" Grid.ColumnSpan="2" Value="{Binding ElementName=rect, Path=(c:BrushExtender.GreenChannel), Mode=TwoWay}" />
<Slider Name="blueSlider" Grid.Column="1" Grid.Row="2" Minimum="0" Maximum="255" Width="200" Height="20" Grid.ColumnSpan="2" Value="{Binding ElementName=rect, Path=(c:BrushExtender.BlueChannel), Mode=TwoWay}" />
<Slider Name="alphaSlider" Grid.Column="1" Grid.Row="3" Minimum="0" Maximum="255" Width="200" Height="20" Grid.ColumnSpan="2" Value="{Binding ElementName=rect, Path=(c:BrushExtender.AlphaChannel), Mode=TwoWay}" />
<Rectangle Fill="SandyBrown" Name="rect" Width="200" Height="50" Grid.Row="4" Grid.ColumnSpan="3" Margin="0,20,0,10"
c:BrushExtender.Brush="{Binding RelativeSource={RelativeSource Self}, Path=Fill, Mode=TwoWay}"/>
<TextBlock Text="Colour Value" Margin="5,0,5,0" Grid.Row="5" HorizontalAlignment="Center" />
<TextBox Text="{Binding ElementName=rect, Path=(c:BrushExtender.ColourValue), Mode=TwoWay}" Margin="0,0,0,0" Grid.Row="5" Grid.Column="1" Width="100" HorizontalAlignment="Center" />
<Button Content="Update" IsEnabled="{Binding ElementName=grid, Path=SelectedItem.SomeValue}"/>
</Grid>
You can easily use the ColorDialog available from classic Windows Forms.
using System.Windows.Forms;
ColorDialog colorDialog = new ColorDialog();
if (colorDialog.ShowDialog() == DialogResult.OK)
{
// do some stuff with colors...
}
I was forced to make such dialog myself. Please look at WPFColorLib - it's FOSS.

Resources