How to disable button when its Button.CommandProperty is null - wpf

Short explanation
Button.CommandProperty is bound to SomeObject.SomeCommand property of the ViewModel. When SomeCommand property of SomeObject is set to null or entire SomeObject property is set to null, this button remains enabled. How can the button be disabled under this circumstances?
Detailed explanation
I am creating application using MVVM, which behaves like a browser:
Main view_model (which corresponds to main window as view) has a list of Workspace view_models. Each Workspace view_model corresponds to TabPage in windows's TabControl.
Main view_model has CurrentWorkspace property which corresponds currently active TabPage.
In main window where is a toolbar with buttons, which utilizes commands provided by the CurrentWorkspace. For example, access to reloading workspace data is realized as:
<Button Name="btReload" Content="Reload"
Command="{Binding Path=CurrentWorkspace.ReloadCommand, UpdateSourceTrigger=PropertyChanged}"/>
I tried to accomplish the task of button disabling by creating DataTriggers, but it seems that triggers works only first time and no more:
<Button Name="btReload" Content="Reload"
Command="{Binding Path=CurrentWorkspace.ReloadCommand, UpdateSourceTrigger=PropertyChanged}">
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=CurrentWorkspace, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="{x:Null}">
<Setter Property="dxb:BarButtonItem.IsEnabled" Value="False"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=CurrentPage.CurrentWorkspace, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}" Value="{x:Null}">
<Setter Property="dxb:BarButtonItem.IsEnabled" Value="False"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
it looks really stupid: like MS Word with documentless client area and at the same time with a lot of ready-for-click buttons in toolbar (with formatting and other document-specific features). Please, help me, :)
P.S. When adding to toolbar a button with DataContext binded to CurrentWorkspace, then its DataContextChanged event fires properly when activating or adding or removing workspace tab in the window. So, the problem somewhere in DataTrigger (or in View as generally), not in it's ViewModel(s).
UPDATE
I uploaded sample project on VS2010, link for the archive: http://www.filefactory.com/file/b43455e/n/WhatIfCommandIsNull.rar
bellow its description.
TextBox is bound to the ViewModel.Data property
Assigning or removing ViewModel into/from Window.DataContext can be done by clicking two buttons - btAssignViewModel and btRemoveViewModel respectively
ViewModel exposes two commands, one of which sets ViewModel.Data to string value, other - sets it to the NULL
These commands are bound to the buttons btSetData & btResetData via their Button.Command properties
As you can see, when Window.DataContext is set to the ViewModel instance, both the commands working properly, & ResetDataCommand.CanExecute is working too (when ViewModel.Data is NULL, ResetDataCommand.CanExecute returns false & button btResetData is disabled). Once the Window.DataContext is set to null, last two buttons enables (for the first two ones trere are no commands are bound).
The problem is to realize declaratively (via triggers) next four rules:
If btAssignViewModel.DataContext is not null then btAssignViewModel.IsEnabled = false, else true.
If btRemoveViewModel.DataContext is null then btRemoveViewModel.IsEnabled = false, else true.
If ViewModel.Data is null then btSetData.IsEnabled = true, else false.
If ViewModel.Data is null then btResetData.IsEnabled = false, else true.
I think that first two rules can be realized using Triggers, second two - using DataTriggers. But they aren't working, so I erased them from the project.

This might work (pending your circumstances)
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<Trigger Property="Command" Value="{x:Null}">
<Setter Property="IsEnabled" Value="false"/>
</Trigger>
</Style.Triggers>
</Style>
</Button.Style>

Related

How do I create a dynamic context menu for ListBoxItems?

I have a style that doesn't seem to be working. In spite of Snoop telling me the DataContext for the ListBoxItem is correct, nothing shows up. If it was a problem with the binding for Commands I would expect to see an empty context menu appear.
The style:
<ContextMenu x:Key="CommandsContextMenu" ItemsSource="{Binding Commands}">
<Style TargetType="MenuItem">
<Setter Property="Header" Value="{Binding Name}"/>
</Style>
</ContextMenu>
<Style TargetType="ListBoxItem">
<Setter Property="ContextMenu" Value="{StaticResource CommandsContextMenu}" />
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding Path="DataContext.HasCommands" />
</DataTrigger.Binding>
</DataTrigger>
</Style.Triggers>
</Style>
The snoop DataContext:
The snoop properties showing the ContextMenu property isn't even set.
The idea here, was that without knowing any of the types, I could have a listbox item style where if the thing it was bound to has a property called HasCommands, and it was set to true, then it would set a context menu on that listbox item, bound to the Commands property.
I'm not getting any binding errors or warnings from PresentationTraceSources.DataBindingSource
Why doesn't the context menu get set?
So it turns out, that the problem was using something that inherited from ListBox
for reference here's my defined class:
// adapted from http://stackoverflow.com/questions/3350187/wpf-c-rearrange-items-in-listbox-via-drag-and-drop
// which was probably adapted from http://wpftutorial.net/DragAndDrop.html
type DragDropListBox() as self =
inherit ListBox()
which from there only hooks the following
self.PreviewMouseLeftButtonUp
self.PreviewMouseMove
override x.OnItemsSourceChanged
and in the intialization it overwrites the ItemContainerStyle as follows:
So it turns out, that the problem was using something that inherited from ListBox
for reference here's the top of my class:
// adapted from http://stackoverflow.com/questions/3350187/wpf-c-rearrange-items-in-listbox-via-drag-and-drop
// which was probably adapted from http://wpftutorial.net/DragAndDrop.html
type DragDropListBox() as self =
inherit ListBox()
which from there only hooks the following
self.PreviewMouseLeftButtonUp
self.PreviewMouseMove
override x.OnItemsSourceChanged
and in the intialization it overwrites the ItemContainerStyle as follows:
do
self.PreviewMouseLeftButtonUp.Add listBoxPreviewMouseLeftButtonUp
//self.PreviewMouseLeftButtonDown.Add listBoxPreviewMouseMove //.AddHandler (MouseButtonEventHandler previewMouseMove)
self.PreviewMouseMove.Add listBoxPreviewMouseMove
let style =
let s = Style(typeof<ListBoxItem>)
s.Setters.Add (Setter(ListBoxItem.AllowDropProperty, true))
s.Setters.Add (EventSetter(ListBoxItem.PreviewMouseLeftButtonDownEvent, MouseButtonEventHandler listBoxPreviewMouseLeftButtonDown))
s.Setters.Add (EventSetter(ListBoxItem.DropEvent, DragEventHandler listBoxItemDrop))
s
self.ItemContainerStyle <- style
now I've got to figure out if you can add two styles together.

WPF Trigger, is it like an if/else?

I've been reading into Triggers lately and am trying to incorporate it into my application but I keep getting the thought that the triggers act like if statements, if this property has a value of this, do this to the control.
What I'm wondering is if we can change a different controls' properties in another trigger? I'm looking to either remove an extra row that is created dynamically or increase the width of a button by a columnspan of 3 only if a button is hidden or specific label text is on the window.
I'm trying to figure out how to do this, but anything I'm trying is requiring me to only change the property of the control that is within the trigger, it won't allow me to change a property of a control outside of the one in the trigger.
Here's what I'm wanting to do:
<Style x:Key="Triggers" TargetType="{x:Type Label}">
<Style.Triggers>
<Trigger Property="Label.Content" Value="Test Label1: ">
<Setter TargetName="Button1" Property="Grid.ColumnSpan" Value="3" /> 'Error 13 Cannot find the Trigger target 'Button1'. (The target must appear before any Setters, Triggers, or Conditions that use it.)
</Trigger>
</Style.Triggers>
</Style>
What my mind is coming up with is the trigger is the IF, the setter is the code within the IF if the logic matches(property is true) - so If the Label contains the text 'Test Label1: " then increase button width by 3 columns. Am I thinking triggers have the functionality they don't and is this possible to do within xaml?
A Trigger is somewhat like an if condition in XAML. However, when using a Trigger to change a property dependant on another control, try to remember this rule of thumb:
Add the Trigger to the control that must do something in reaction to a change in another control
In your case, you want the Button to span some columns when the Label.Content = Test Label1:, so you'd need to apply the Trigger to the Button instead. Furthermore, to read the value of the Label.Content from the Button, you'll need to use a DataTrigger. Try this:
<Button Content="Something">
<Button.Style>
<Style TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding Content, ElementName=LabelName}"
Value="Test Label1: ">
<Setter Property="Grid.ColumnSpan" Value="3" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>

Set property after command

I have a StatusBar below the screen in SL4 (using PRISM), just a very simple Telerik RadDockPanel.
I also have a menu (Telerik RibbonView with RadRibbonGroup and RadRibbonToggleButton). When the toggle button is pressed, I want to set the text to 'ON' and 'OFF', and I want to hide the status bar, but... only in XAML (not using code behind).
I believe this is a common SL/WPF coding practice... but how ?
Have to use EventTrigger (check example bellow on page by link that I provided) and ObjectAnimationUsingKeyFrames to change properties that aren't animated (Text, Visibility so on).
Check good example in other answer on so.
You can specify a DataTrigger in your window like this -
<StatusBar.Style>
<Style>
<Style.Triggers>
<DataTrigger
Binding="{Binding ElementName=MyRadRibbonToggleButton, Path=IsChecked}"
Value="True">
<Setter Property="Grid.Visibility" Value="Collapsed" />
</DataTrigger>
</Style.Triggers>
</Style>
</StatusBar.Style>
In case you can't use ElementName binding then you can use a property in your ViewModel(corresponding to RadRibbonToggleButton state). Similar Trigger can be created for a TextBlock/Label to show On/Off text.
This is how I implement this kind of functionality in WPF/MVVM applications;
You may have to apply some hack to make this work with telerik controls.

Let one control be enabled/disabled based on another control

I have two DataGrid's that I want do have enabled/disabled based on whether precisely 1 element is selected in another DataGrid. What is the simplest way to accomplish this dependency control in WPF?
You could use a trigger:
<DataGrid.Style>
<Style TargetType="DataGrid">
<Setter Property="IsEnabled" Value="False"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedItems.Count,
ElementName=datagrid1}"
Value="1">
<Setter Property="IsEnabled" Value="True"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.Style>
You could:
Create an IValueConverter, perhaps it is called NotEqualToOneBooleanConverter
Bind IsEnabled from one DataGrid to the SelectedItems.Count on the other
Set the Converter on this Binding to be the NotEqualToOneBooleanConverter
This approach is nice since once your converter is created, it can be applied throughout your XAML and to any type and any property (not just DataGrid or SelectedItems.Count). To make it even more flexible, you could have a more generic version of this converter that could compare any two values specified directly from XAML (one from the Binding and one specified as property on the Converter).
The downside to this approach - it's XAML only, and difficult to test especially if what you are trying to achieve is a business requirement and not just a graphical effect.
Hope this helps!
This is my quick hack:
tablesControl.SelectionChanged += (sender, sce) =>
{
var c = tablesControl.SelectedItems.Count;
var orderingPossible = c == 1;
itemsControl.IsEnabled = orderingPossible;
};
In the first Grid have an event or Command that is fired when you click on that cell, in this event you need to have some bool property you can set to false, then bind the Enabled property to this bool. If you are using MVVM this will be very easy, have a look at this to see how - http://www.youtube.com/watch?v=tKfpvs7ZIyo

WPF/MVVM: Disable a Button's state when the ViewModel behind the UserControl is not yet Initialized?

I have a DocumentListView.Xaml with a ListBox and 3 Buttons.
Behind that UserControl sits a DocumentListViewModel with 3 Buttons and their Command Property bound to 3 RelayCommands.
I have 3 Controller like AdministrationController, BillingController, ReportController.
Every Controller has ObservableCollections like Customer 1 : N Order 1: N Document same for the other Controller.
In one Controller I have a special binding situation. When my DocumentListViewModel is not initialized by its parent ViewModel like OrderViewModel (because no orders are loaded/exist) then my UserControl has 3 buttons which are ENABLED. Ok the user can press the 3 buttons and nothing happens but still its very confusing and above all the consistency in my user interface is gone.
How can I set the Command of a Button as default to "Disabled" ?
Setting the Buttons IsEnabled property to false does not help because the button will stay forever in the disabled state. No CanExecute TRUE will set it to IsEnabled = true.
AND I do not want to introduce another property IsButtonEnabled... that stupid because then I have both worlds winforms and wpf behind my buttons logic... ICommand should be enough.
Or you can use a Style for the button to disable:
<Style TargetType="{x:Type Button}" x:Key="DisablerButton">
<Style.Triggers>
<Trigger Property="Command" Value="{x:Null}">
<Setter Property="IsEnabled" Value="False" />
</Trigger>
</Style.Triggers>
</Style>
This is an interesting situation. Honestly I've never run into the case where the UI was loaded and interactive but the ViewModel was not yet bound.
However, ignoring that for a moment, you could potentially use a FallbackValue on your binding to bind to a globally available NullCommand or something that always returns false for its CanExecute method.
<Button Command="{Binding SaveCommand, FallbackValue={StaticResource NullCommand}}" />

Resources