Disable button while processing request - wpf

I am new to wpf and xaml (Windows development in general) and my background is asp.net and prior to that classic ASP.
I'm working on an application that needs to have the button disabled/grayed out while the processing occurs and read a post on here to do the following but it doesn't appear to be working. What am I missing?
<Window x:Class="SCGen.Application.LoadForecast.EngineExecution"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:igEditors="http://infragistics.com/Editors"
SizeToContent="WidthAndHeight"
Title="Engine Execution"
ResizeMode="NoResize"
WindowStartupLocation="CenterOwner"
Background="{StaticResource {x:Static SystemColors.ControlBrushKey}}">
<Window.Resources>
<Style TargetType="{x:Type Button}" x:Key="myStyle" BasedOn="{StaticResource ButtonStyle}">
<Setter Property="Command" Value="{Binding ExecuteEngine}" />
<Setter Property="Content" Value="Execute Engine" />
<Style.Triggers>
<Trigger Property="Command" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False"/>
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Border Padding="8">
<StackPanel>
<StackPanel MaxWidth="200" HorizontalAlignment="Left">
<TextBlock Text="Select Forecast Engine" TextAlignment="Center" FontSize="13" />
<igEditors:XamComboEditor ItemsSource="{Binding ForecastEngines}" SelectedItem="{Binding SelectedEngine}" Margin="0,5" />
<Button Style="{StaticResource ResourceKey=myStyle}" />
</StackPanel>
<TextBlock Text="{Binding EngineStatus}" FontSize="15" FontStyle="Italic" Margin="0,14" Width="400" TextWrapping="Wrap" />
</StackPanel>
</Border>
</Window>
I've changed the xaml to the following:
<Button Content="Execute Weather Import" Command="{Binding ExecuteWeather}" Style="{StaticResource ButtonStyle}" IsEnabled="{Binding IsEnabled}"/>
In the ViewModel I have the following:
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set { _isEnabled = value; }
}
and I set the _isEnabled here:
private string LaunchWeatherImport(string strVendor)
{
_isEnabled = false;
string uri = ConfigurationManager.AppSettings["ManualExecutionFacilitatorService"];
ClientConnectionInfo connection = new ClientConnectionInfo(uri) { UseSecurity = true };
connection.SetTimeouts();
Logger.LogInfo("Calling Facilitator service to manually import " + strVendor + " weather data.");
((NetTcpBinding)connection.Binding).Security.Mode = System.ServiceModel.SecurityMode.None;
using (var client = new FacilitatorManualExecutionClient(connection))
{
client.InnerChannel.OperationTimeout = TimeSpan.FromMinutes(int.Parse(ConfigurationManager.AppSettings["OperationTimeOutMinutes"]));
try
{
_isEnabled = true;
return "success";
// uncomment this line before commit
//return client.ExecuteWeather(strVendor);
}
#region catch
catch (Exception ex)
{
Logger.LogError(ex.Message, ex);
return ex.Message;
}
#endregion
}
}
I still can't get it to work properly.

For starters, you're setting the trigger on the Command property but you don't have a binding set on that property for your button:
<Button Style="{StaticResource ResourceKey=myStyle}" />
Should be:
<Button Style="{StaticResource ResourceKey=myStyle}" Command="{Binding MyCommand}" />
[Where MyCommand is the name of your actual command that you're binding to]
I am not so sure that it will work anyway though because your trigger is set to fire when the Command property is null, but if you bind to the command property, following the MVVM pattern then your command property shouldn't be null so the trigger won't fire then either.
UPDATE:
You need to implemented the INotifyPropertyChanged interface on your class that has the property.
public class MyClass : System.ComponentModel.INotifyPropertyChanged
Then add the implementation:
public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
Then change your property to be:
private bool _isEnabled = true;
public bool IsEnabled
{
get { return _isEnabled; }
set
{
_isEnabled = value;
NotifyPropertyChanged("IsEnabled");
}
}

Related

How to disable unchecked checkbox in listbox wpf?

I have 30 checkbox. If i checked any 6 checkbox, remaining 24 checkbox should be disabled and if i unchecked any one checkbox from 6, all checkbox should be enabled.
<Style TargetType="{x:Type ListBox}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate>
<Border BorderBrush="Black" BorderThickness="4"
CornerRadius="5" Margin="6"
>
<CheckBox Uid="checkbox1" Name="checkbox1" Checked="CheckBox_Checked" Unchecked="CheckBox_UnChecked" IsChecked="{Binding ElementName=button,Path=IsChecked,Mode=OneWay}">
<Image
Source="{Binding Path=UriSource}"
Stretch="Fill"
Width="100" Height="120"
/>
</CheckBox>
</Border>
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<WrapPanel />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/>
<ListBox Name="List1" ItemsSource="{Binding}" Margin="-50,-8,93,0" RenderTransformOrigin="0.5,0.5" Height="289" VerticalAlignment="Top" >
<ListBox.RenderTransform>
<TransformGroup>
<ScaleTransform ScaleX="0.975" ScaleY="0.997"/>
<SkewTransform AngleY="-8.98" AngleX="9.705"/>
<RotateTransform Angle="9.419"/>
<TranslateTransform Y="76.889" X="64.258"/>
</TransformGroup>
</ListBox.RenderTransform>
</ListBox>
c#:
if (iList.Count == 6) { List1.IsEnabled = false; }
it disables whole listbox.
ilist contains collection of checked checkbox values.
any method to do this?
please help me.
If you want to disable the checkboxes individually i would suggest binding to their IsEnabled property.You can iterate through your list everytime a listbox is checked or unchecked to see if 6 items are checked. If they are you can just iterate through the list again and set the now binded IsEnabled property to false for every item that isn't already checked. I guess you are working in code behind, but i would recommend doing it with a viewmodel. This would look something like this:
ViewModel
public class TestViewModel
{
private bool _disabledCheckBoxes;
public TestViewModel()
{
TestList = new ObservableCollection<CheckBoxItem>();
for (int i = 0; i < 30; i++)
{
TestList.Add(new CheckBoxItem(true));
}
}
public ObservableCollection<CheckBoxItem> TestList { get; set; }
public RelayCommand CheckBoxChanged
{
get { return new RelayCommand(OnCheckBoxChanged); }
}
private void OnCheckBoxChanged()
{
if (_disabledCheckBoxes)
{
foreach (var item in TestList)
{
item.IsEnabled = true;
}
_disabledCheckBoxes = false;
}
else
{
int i = 0;
foreach (var item in TestList)
{
if (item.IsChecked)
i++;
}
if (i >= 6)
{
foreach (var item in TestList)
{
if (!item.IsChecked)
item.IsEnabled = false;
}
_disabledCheckBoxes = true;
}
}
}
}
CheckBoxItem
public class CheckBoxItem : ObservableObject
{
public CheckBoxItem(bool isEnabled)
{
IsEnabled = isEnabled;
}
private bool _isEnabled;
public bool IsEnabled
{
get
{
return _isEnabled;
}
set
{
_isEnabled = value;
RaisePropertyChanged("IsEnabled");
}
}
private bool _isChecked;
public bool IsChecked
{
get
{
return _isChecked;
}
set
{
_isChecked = value;
RaisePropertyChanged("IsChecked");
}
}
}
Xaml Code / View
<Window x:Class="WpfAppTests.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:WpfAppTests"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
xmlns:modelNoMvvmLight="clr-namespace:WpfAppTests"
xmlns:modelMvvmLight="clr-namespace:WpfAppTests.ViewModel"
Loaded="Loaded_Window"
Title="MainWindow" Height="350" Width="525" >
<Window.DataContext>
<modelMvvmLight:TestViewModel/>
</Window.DataContext>
<StackPanel>
<ListBox ItemsSource="{Binding TestList}">
<ListBox.ItemTemplate>
<DataTemplate>
<CheckBox IsChecked="{Binding IsChecked}" IsEnabled="{Binding IsEnabled}" >
<i:Interaction.Triggers>
<i:EventTrigger EventName="Checked">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.CheckBoxChanged}"/>
</i:EventTrigger>
<i:EventTrigger EventName="Unchecked">
<i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=Window}, Path=DataContext.CheckBoxChanged}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</CheckBox>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</Window>
I bound the Checked and UnChecked event with the command CheckBoxChanged(in the View Model) so every time the state of a checkbox changes, your requirement (checked items == 6) can be verified.
I used MvvmLight in my example, but if you consider using the mvvm pattern and don't want to use a framework you can implement ObservableObject and RelayCommand yourself.

Binding Dependency Property of UserControl to MainWindow ViewModel in WPF

Allow me to simplify the problem to its basic blocks.
I have a UserControl1 in my project. It has a TextBox like this:
<TextBox Text="{Binding TextProperty}"/>
In the code-behind, I have a dependency property like this:
public string TextProperty
{
get { return (string)GetValue(TextPropertyProperty); }
set { SetValue(TextPropertyProperty, value); }
}
public static readonly DependencyProperty TextPropertyProperty = DependencyProperty.Register("TextProperty", typeof(string), typeof(UserControl1), new PropertyMetadata(null));
And the constructor of the UserControl is simply
public UserControl1()
{
InitializeComponent();
DataContext = this;
}
In the MainWindow, I have this:
<userctrl:UserControl1 TextProperty="{Binding ABC, UpdateSourceTrigger=PropertyChanged}"/>
<TextBox Grid.Row="1" Text="{Binding PQR, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}"/>
Now, in the viewModel of the MainWindow, I have:
private string _abc;
public string ABC
{
get { return _abc; }
set
{ _abc = "20";
OnPropertyChanged();
}
}
private string _pqr;
public string PQR
{
get { return _pqr; }
set
{
if(value != _pqr)
{
_pqr = value;
ABC = PQR;
OnPropertyChanged();
}
}
}
Now, even the UserControl is supposed to replicate whatever I write in the textbox, right?
But it doesn't. I have set breakpoints at setter of ABC. It gets updated, but that update does not reach the UserControl. My gut says that the binding is failing silently, and it's because I have set the DataContext to this in the constructor of UserControl.
How am I supposed to solve it?
The Actual Scenario:
Here is the XAML for the UserControl:
<UserControl x:Class="MyDiskTools.UserControls.NodeGrid.NodeGrid"
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:local="clr-namespace:MyDiskTools.UserControls.NodeGrid"
mc:Ignorable="d">
<Grid>
<Grid.Resources>
<Style TargetType="Button">
<Setter Property="Padding" Value="5"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Command" Value="{Binding InputCommand}"/>
<Setter Property="CommandParameter" Value="{Binding Path=Content, RelativeSource={RelativeSource Self}}"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderThickness" Value="5"/>
<Setter Property="FontSize" Value="20"/>
<Setter Property="FontFamily" Value="Times New Roman"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<UniformGrid Grid.Row="0" Rows="1">
<Button Content="A" />
<Button Content="B" />
<Button Content="C" />
<Button Content="D" />
<Button Content="E" />
<Button Content="F" />
</UniformGrid>
<UniformGrid Grid.Row="1" Rows="1">
<Button Content="G" />
<Button Content="H" />
<Button Content="I" />
<Button Content="J" />
<Button Content="K" />
<Button Content="L" />
<Button Content="M" />
</UniformGrid>
<UniformGrid Grid.Row="2" Rows="1">
<Button Content="N" />
<Button Content="O" />
<Button Content="P" />
<Button Content="Q" />
<Button Content="R" />
<Button Content="S" />
<Button Content="T" />
</UniformGrid>
<UniformGrid Grid.Row="3" Rows="1">
<Button Content="U" />
<Button Content="V" />
<Button Content="W" />
<Button Content="X" />
<Button Content="Y" />
<Button Content="Z" />
</UniformGrid>
<TextBox Name="InputMessage" HorizontalContentAlignment="Center" VerticalContentAlignment="Center" IsEnabled="False" Background="Beige" Grid.Row="4" Text="{Binding PasswordDisplay, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</Grid>
</UserControl>
Here is the code-behind:
public partial class NodeGrid : UserControl
{
public NodeGrid()
{
InitializeComponent();
InputCommand = new InputCharacterCommand(this);
DataContext = this;
}
public string PasswordDisplay
{
get { return (string)GetValue(PasswordDisplayProperty); }
set { SetValue(PasswordDisplayProperty, value); }
}
public static readonly DependencyProperty PasswordDisplayProperty =
DependencyProperty.Register("PasswordDisplay", typeof(string), typeof(NodeGrid), new PropertyMetadata(""));
private ICommand _inputCommand;
public ICommand InputCommand
{
get
{
return _inputCommand;
}
set
{
_inputCommand = value;
}
}
public void AddCharacter(string input)
{
if (input != null)
{
PasswordDisplay = string.Concat(PasswordDisplay, input);
}
}
public bool InputAllowed()
{
if (PasswordDisplay == null)
{
return true;
}
if (PasswordDisplay.Length < 50)
{
return true;
}
return false;
}
private void OnPropertyChange([CallerMemberName] string property = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(property));
}
public event PropertyChangedEventHandler PropertyChanged;
}
class InputCharacterCommand : ICommand
{
private NodeGrid _vmodel;
public InputCharacterCommand(NodeGrid vm)
{
_vmodel = vm;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return (_vmodel.InputAllowed());
}
public void Execute(object parameter)
{
_vmodel.AddCharacter(parameter as string);
}
}
And here is how I use it in my MainWindow:
<StackPanel VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
<WrapPanel HorizontalAlignment="Center">
<Label Content="Enter PIN"/>
<TextBox CommandManager.PreviewCanExecute="HandleCanExecute" Foreground="Transparent" MinWidth="100" Padding="10,0" Text="{Binding PIN, UpdateSourceTrigger=PropertyChanged}"/>
</WrapPanel>
<customlock:NodeGrid MinHeight="250" MinWidth="500" PasswordDisplay="{Binding NodeGridDisplay}"/>
<Button VerticalAlignment="Center" HorizontalAlignment="Center" Content="Unlock!"/>
</StackPanel>
The Code-behind:
private void HandleCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
if (e.Command == ApplicationCommands.Cut ||
e.Command == ApplicationCommands.Copy ||
e.Command == ApplicationCommands.Paste)
{
e.CanExecute = false;
e.Handled = true;
}
}
And now, the ViewModel:
private string _PIN;
public string PIN
{
get
{
return _PIN;
}
set
{
if(value != _PIN)
{
_PIN = value;
OnPropertyChanged();
NodeGridDisplay = HashIt(_PIN);
}
}
}
private string _nodeGridDisplay;
public string NodeGridDisplay
{
get
{
return _nodeGridDisplay;
}
set
{
if (value != _nodeGridDisplay)
{
_nodeGridDisplay = value;
OnPropertyChanged();
}
}
}
private string HashIt(string input)
{
using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create())
{
return System.Text.Encoding.Default.GetString(md5.ComputeHash(System.Text.Encoding.ASCII.GetBytes(input))).GetHashCode().ToString();
}
}
What is the intended function?
Let me illustrate it.
The Lock
One can enter the PIN, the hash of which will be displayed in the NodeGrid. Then the user will click the letters which will be concatenated with the hash. Then the user can click unlock.
You should use ElementName in your UserControl or use RaltiveResource to find it, and you should not use DataContext = this. (see this answer and this link)
Give your userControl a name and write:
<UserControl x:Class= ......
Name="userControl">
<TextBox Text="{Binding Text, ElementName = userControl}"/>
</UserControl>
Please note that you should use Text instead of TextProperty and make sure to change _abc = "20" to _abc = value.

ContentPresenter's Binding not updated when bound object is updated

I have a master view and two subviews. I would like to switch from SubViewA to SubViewB when clicking on the button on SubViewA. The masterview contains a contentpresenter which is binded to View, and initialized to SubViewB when loaded. When clicking on the button on SubViewA the SubViewB constructor is called, but the control is never loaded. What am I missing? I've also tried by just setting the contenttemplate:
<ContentPresenter x:Name="contentPresenter" Content="{Binding View, PresentationTraceSources.TraceLevel=High}" />
which does not work either.
MainWindow:
<Window x:Class="WpfApplication2.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"
xmlns:local="clr-namespace:WpfApplication2">
<Grid>
<TextBlock Text="MasterViewPage" />
<ContentControl x:Name="content" Content="{Binding View}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:SubViewModelA}">
<local:SubViewA></local:SubViewA>
</DataTemplate>
<DataTemplate DataType="{x:Type local:SubViewModelB}">
<local:SubViewB></local:SubViewB>
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
</Grid>
</Window>
public partial class MainWindow
{
public MainWindow()
{
Loaded += MainWindow_Loaded;
InitializeComponent();
}
private void MainWindow_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
DataContext = new MainViewModel();
}
}
SubViewA:
<UserControl x:Class="WpfApplication2.SubViewA"
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="300" d:DesignWidth="300">
<Grid Margin="0,40,0,0">
<TextBlock Text="Subview A" />
<Button Height="50" Width="120" Content="Open View B" Command="{Binding OpenViewCommand}" />
</Grid>
</UserControl>
public partial class SubViewA
{
public SubViewA()
{
Loaded += SubViewA_Loaded;
InitializeComponent();
}
private void SubViewA_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
DataContext = new SubViewModelA();
}
}
ViewModels:
public class MainViewModel : NotifyPropertyChanged
{
private object _view;
public object View
{
get { return _view; }
set
{
_view = value;
RaisePropertyChanged(() => View);
}
}
public MainViewModel()
{
View = new SubViewA();
}
}
public class SubViewModelA : MainViewModel
{
public ICommand OpenViewCommand
{
get { return new DelegatingCommand(OpenView); }
}
private void OpenView()
{
View = new SubViewB();
}
}
public class SubViewModelB : MainViewModel
{
}
Thanks in advance.
View-models should not contain references to the view, instead have a property ViewMode which can be an enum and trigger on that, here is an example (you can set the ContentTemplate instead of Content as well).
OK, the solution that worked for me was:
MainView.xaml:
<ContentControl x:Name="content">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding View, PresentationTraceSources.TraceLevel=High}" Value="SubViewA">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<local:SubViewA />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding View, PresentationTraceSources.TraceLevel=High}" Value="SubViewB">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<local:SubViewB />
</DataTemplate>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
and in SubViewA.xaml:
<Button Height="50" Width="120" Content="Open View B" Command="{Binding Path=DataContext.OpenViewCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}, PresentationTraceSources.TraceLevel=High}" />
Reason was that View wasn't set on MainViewModel, but on the subview instead (which inherited from MainViewModel). When removing the inheritance to actually set the view on MainViewModel everything worked.
I can agree on that it's not good to set the view in model directly. But anyway, I tried your solution (both of them) and SubViewB is still not loaded. The constructor is called, but never SubViewB_Loaded. So, the result is that the SubViewB is never shown.
The datacontextchanged is never triggered on the contentcontrol. So, still, I'm missing something.
The main view:
<Window x:Class="WpfApplication2.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"
xmlns:local="clr-namespace:WpfApplication2">
<Grid>
<TextBlock Text="MasterViewPage" />
<ContentControl x:Name="content">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Style.Triggers>
<DataTrigger Binding="{Binding View}" Value="SubViewA">
<Setter Property="Content">
<Setter.Value>
<local:SubViewA />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding View}" Value="SubViewB">
<Setter Property="Content">
<Setter.Value>
<local:SubViewB />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</Grid>
</Window>
And in the viewmodel:
public class MainViewModel : NotifyPropertyChanged
{
private string _view;
public string View
{
get { return _view; }
set
{
_view = value;
RaisePropertyChanged(() => View);
}
}
public MainViewModel()
{
View = "SubViewA";
}
}
public class SubViewModelA : MainViewModel
{
public ICommand OpenViewCommand { get { return new DelegatingCommand(OpenView); } }
private void OpenView()
{
View = "SubViewB";
}
}
public class SubViewModelB : MainViewModel
{
}

Databinding to Command in Silverlight Templated Button control

I am trying to create a templated button control with databinding for the Visibility, tooltip, and Command. The Visibility binding works, as does the tooltip, but the Command does not. Another process is responsible for injecting the viewmodel and associating it with the View, and the other data bindings are working so I am pretty confident that is working properly.
In the resource dictionary:
<Converters:BoolToVisibilityConverter x:Key="boolVisibilityConverter" />
<Style TargetType="local:ImageButton">
<Setter Property="Visibility" Value="{Binding FallbackValue=Visible, Path=ToolIsAvailable, Converter={StaticResource boolVisibilityConverter} }"/>
<Setter Property="Command" Value="{Binding ButtonCommand}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageButton">
<Grid>
<Image Source="{TemplateBinding Image}"
ToolTipService.ToolTip="{Binding ToolName}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
the templated control
public class MyButton: ImageButton
{
public MyButton(MyCommandViewModel viewmodel)
{
this.DefaultStyleKey = typeof(ImageButton);
this.Image = new BitmapImage(new Uri("/MyProject;component/Themes/myimage.png", UriKind.Relative));
this.DataContext = viewmodel;
}
}
and in the view model
public MyCommandViewModel()
: base("My Tool", true)
{
}
public class CommandViewModel
{
public CommandViewModel(string toolName, bool isAvailable)
{
ToolIsAvailable = isAvailable;
ToolName = toolName;
_buttoncommand = new DelegateCommand(() =>
{
ExecuteCommand();
},
() => { return CanExecute; });
}
private bool _canExecute = true;
public bool CanExecute
{
get { return _canExecute; }
set
{
_canExecute = value;
OnPropertyChanged("CanExecute");
if (_command != null) _command.RaiseCanExecuteChanged();
}
}
private DelegateCommand _buttoncommand;
public ICommand ButtonCommand
{
get { return _buttoncommand; }
}
protected virtual void ExecuteCommand()
{
}
public bool ToolIsAvailable
{
get { return _toolIsReady; }
set { _toolIsReady = value; OnPropertyChanged("ToolIsAvailable"); }
}
public string ToolName
{
get { return _toolName; }
set { _toolName = value; OnPropertyChanged("ToolName"); }
}
}
Why are the other databindings functioning properly but not the Command data binding. I found this similar post
Overriding a templated Button's Command in WPF
Do I need to template a grid control instead and use RoutedCommands? I am not sure I understand why Silverlight treats the Command binding different than the others so I suspect I just have a bug in the code.
Does specifically looking for the datacontext work?
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type UserControl}}, Path=DataContext.ButtonCommand}"
This was my solution. Using the same commandviewmodel as above and same MyCommandViewModel
<Style TargetType="local:ImageButton">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:ImageButton">
<Grid>
<Image Source="{TemplateBinding Image}" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The databinding is now done in a user control
<UserControl x:Class="SilverlightApplication11.Test"
...
>
<UserControl.Resources>
<Converters:BoolToVisibilityConverter x:Key="boolVisibilityConverter" />
</UserControl.Resources>
<Grid>
<local:ImageButton Image="/SilverlightApplication11;component/Themes/hand.png" Command="{Binding ButtonCommand}" Visibility="{Binding FallbackValue=Visible, Path=ToolIsAvailable, Converter={StaticResource boolVisibilityConverter} }"/>
</Grid>
</UserControl>
and the code behind
public Test(TestCommandViewModel vm)
{
InitializeComponent();
this.Loaded += (o, e) => this.DataContext = vm;
}

How to get WPF DataBinding-to-an-object to work

In the following example I bind the XAML to a static object via ObjectDataProvider.
When the user changes information, I want it to automatically reflect in the XAML.
What I don't understand is:
how do I perpetuate the object? do I have to create a singleton? in the click event, how do I access the "object that is being edited"
eventually of course I want the data to be retrieved from a model which reads an XML file or web service, and I want of course my ViewModel to check my model every second or so to see if the data has changed and reflect this on the XAML.
How to I get THERE from HERE:
XAML:
<Window x:Class="TestBinding99382.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestBinding99382"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider
x:Key="DataSourceCustomer"
ObjectType="{x:Type local:Customer}" MethodName="GetCustomer"/>
<Style x:Key="DataRowStyle" TargetType="StackPanel">
<Setter Property="Orientation" Value="Horizontal"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<Setter Property="Margin" Value="0 10 0 0"/>
<Setter Property="DataContext"
Value="{StaticResource DataSourceCustomer}"/>
<Setter Property="DockPanel.Dock" Value="Top"/>
</Style>
</Window.Resources>
<DockPanel>
<StackPanel DockPanel.Dock="Top"
DataContext="{StaticResource DataSourceCustomer}"
Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding Path=LastName}"/>
<TextBlock Text=" ("/>
<TextBlock Text="{Binding Path=FullName}" FontWeight="Bold"/>
<TextBlock Text=")"/>
</StackPanel>
<StackPanel Style="{StaticResource DataRowStyle}">
<TextBlock Text="First Name:"/>
<TextBox Text="{Binding Path=FirstName}"
Width="200" Margin="3 0 0 0"/>
</StackPanel>
<StackPanel Style="{StaticResource DataRowStyle}">
<TextBlock Text="Last Name:"/>
<TextBox Text="{Binding Path=LastName}"
Width="200" Margin="3 0 0 0"/>
</StackPanel>
<StackPanel Style="{StaticResource DataRowStyle}">
<Button Content="Save Changes" Click="Button_Click"/>
</StackPanel>
</DockPanel>
</Window>
Code Behind:
using System.Windows;
using System.ComponentModel;
using System;
namespace TestBinding99382
{
public partial class Window1 : Window
{
private Customer _customer;
public Window1()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
//I want to edit the _customer object here
//and have it my changes automatically reflect in my XAML
//via the INotifyPropertyChanged inheritance.
}
}
public class Customer : INotifyPropertyChanged
{
private string _firstName;
private string _lastName;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
this.RaisePropertyChanged("FirstName");
this.RaisePropertyChanged("FullName");
}
}
public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
this.RaisePropertyChanged("LastName");
this.RaisePropertyChanged("FullName");
}
}
public string FullName
{
get
{
return String.Format("{0} {1}", _firstName, _lastName);
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
public static Customer GetCustomer()
{
return new Customer { FirstName = "Jim", LastName = "Smith" };
}
}
}
in the click event, how do I access
the "object that is being edited"
You can access a resource in the behind code using FindResource method, see below.
private void Button_Click(object sender, RoutedEventArgs e)
{
ObjectDataProvider objectDataProvider
= FindResource("DataSourceCustomer") as ObjectDataProvider;
_customer = objectDataProvider.Data as Customer;
}
For your other questions:
What is perpetuate? You do not have to create a singleton to databind in WPF if that is your question.
eventually of course I want the data to be retrieved from a model which reads an XML file or web service, and I want of course my ViewModel to check my model every second or so to see if the data has changed and reflect this on the XAML.
WPF databinding will automatically update your view of you use an INotifyPropertyChanged object. Unless for performance reasons you only want to update your view every second or so just stick to normal databinding.

Resources