Binding Visibility to Text.Length - wpf

I implemented a small visual indicator (just a textblock with a border), that should be hidden if there is no text to be shown at the moment. The text is bound to the Indicator property, the data context seems to be set correctly.
What I got so far is this (indicator text appears, hide/show doesn't work):
<Border>
<Border.Style>
<Style TargetType="{x:Type Border}">
<Setter Property="Visibility" Value="Visible"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=Indicator.Length}" Value="0">
<Setter Property="Visibility" Value="Hidden"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
<TextBlock Text="{Binding Indicator}" />
</Border>
My problem is that the element is not hidden if the text length is zero.
Do you spot my mistake?
Indicator is part of the corresponding viewmodel:
public string Indicator
{ get; set;}
UPDATE
It works if I change the property above to this:
public const string IndicatorPropertyName = "Indicator";
private string _indicator = "";
public string Indicator
{
get
{ return _indicator;}
set
{
if (_indicator == value) { return;}
RaisePropertyChanged(IndicatorPropertyName);
}
}
Why does it only work, if I raise PropertyChanged event?

I think as the name implies a Trigger only executes or checks its state when an event occurs.
In case of a DataTrigger it is the PropertyChanged-Event of the interface INotifyPropertyChanged.
Wihout raising the Event the DataTrigger doesn´t know that he has to check the binding and if the value meets the trigger condition.

Your source binding is Indicator which is a plain CLR property.
WPF is based heavily on data binding. Bound objects update automatically when the binding source changes because under the hood a change notification is raised. When bounding to a dependency property (that is a property type understood only by WPF) you already have built in change notification for bound objects. No extra work is required.
You can also bound to plain CLR properties but in this case there is no built-in change notification for bound objects. You need to implement the change notification programmatically. This is done by raising the PropertyChanged event. The objects bound to that CLR property will be notified of value change and will update themselves.
In short if you want a CLR property to update a target binding, you need to implement INotifyPropertyChanged and raise the PropertyChanged event.

Related

Hide ListBoxItems not working

I have a Textbox and a ListBox
<TextBox FontSize="12pt" Text="{Binding NameFilter, UpdateSourceTrigger=PropertyChanged}" />
<ListBox x:Name="EmployeeList" ItemsSource="{Binding EmployeeList}">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsVisible}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsVisible}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
The Property of my ViewModel
public string NameFilter
{
get { return _nameFilter; }
set {
_nameFilter = value;
FilterEmployees();
}
}
FilterEmployees set the Property IsVisible of each Employee to true or false.
I have two Problems:
1.)
My Setter gets called after each user input and my EmployeeList gets filtered.
this.RaisePropertyChanged(() => this.EmployeeList);
Gets called. But I see no updates to my list.
2.)
When I manually Update my List (through another function) the Items dissapear. But they never reapear. My filter set everything to IsVisible=true, when the input string is empty (checked it inside the debugger). But not UI update happens.
What am I missing?! I used this answer, but that seems not to be my problem, since it works if I trigger another Action inside my program that updates the list:
WPF - hiding listbox items
EDIT:
I am sorting my Items (actually moving them inside the ObsservableCollection). This way the changes get visible (Employees are hidden). But this just works with hiding, they never reaper.
It seems like the whole stuff gets triggered to late. I want only employees with a "m" inside their name. I have to manually refresh the List two times. Sometimes there are still some Users left, who don't have a "m" inside their name.
You can filter the bound collection of List through CollectionView. Your implementation can be as follows : Get CollectionView for ListBox's ItemsSource and define the filter delegate. Using CollectionView and Filter you are not updating the actual ItemSource but putting a filter for what to show based on filter predicate.
private void FilterEmployees()
{
ICollectionView items = CollectionViewSource.GetDefaultView(EmployeeList);
if (items != null)
{
items.Filter = SearchFilter;
}
}
You can store as the CollectionView member variable so that you don't need to get the CollectionView again and again
public bool SearchFilter(object filterObject)
{
var filter = filterObject as <<List Box item type>>;
if (filter == null)
{
return false;
}
<<Your search logic here.......>>
}
Also, the type of EmployeeList should be ObservableCollection which I found in your edit that you did that.
Now, question here about --- do you have to search on each key stroke or you want to delay the search so that it allows user to type. So to implement this you can create a attach/dependency property to define a delay so that search function will be invoked after defined duration. You have follow the approach mentioned in this link.

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

The MVVM way of presenting controls dynamically

I have a need to display some kind animation as different processes gets started. My initial idea was to simply add some <ContentControl> tags to the XAML and bind them to a property in the View Model object which then simply assigned this property a ProgressBar, some busy spinner or whatever.
This works but I don't like it. The primary reason I don't like it is because the View Model should not involve itself in presentation matters and this pattern clearly breaks that paradigm.
This is pretty much what my (ugly) code looks like atm:
XAML:
<ContentControl Content="{Binding ProcessAAnimation}" />
In View Model class:
public object ProcessAAnimation
{
get { return _processAAnimation; }
private set
{
_processAAnimation = value;
OnPropertyChanged("ProcessAAnimation");
}
}
public object IsProcessARunning
{
get { return _processARunning; }
private set
{
if (value == _processARunning)
return;
_processRunnings = value;
if (value)
ProcessAAnimation = SomeNiftyAnimationControl();
else
{
if (ProcessAAnimation is IDisposable)
((IDisposable)ProcessAAnimation).Dispose();
ProcessAAnimation = null;
}
}
}
// (clipped: More properties for "Process B", "Process C" and so on)
So, is there a better pattern to achieve this. Preferrably, a pattern where I can create my animation controls dynamically using XAML alone?
Please note that I have already tested a solution where I declare three different animation controls and then bind their Visibility property to the View Model state. That, however, is below par in my book because I don't want to just hide the controls, I want them to be gone unless needed. Besides, that would also make it impossible to dynamically use different types of animations for whatever needs may be.
Anyone?
Well your ViewModel knows about the operation and the progress itself. The rest can be accomplished via Triggers. At least thats the way we do it. So your ViewModel has a property "IsLoadingImage" for example, which is set when your viewmodel starts a BackgroundWorker for loading a big image, it also returns the progress reported by the BackgroundWorker "ImageLoadingProgress" now these two properties are enough to pass to your View. Your view, consists of a Progress bar or a custom control for your special animation. You could now bind the "IsLoadingImage" in a Trigger to toggle the ProgressBar/Animation control visibility and the Value of these is bound to "ImageLoadingProgress".
Like i said, thats how we handle it, and our application makes heavy use of MVVM.
Edit respond to a comment: How to change the template in a trigger
<ControlTemplate x:Name="ActiveTemplate" TargetType="{x:Type MyType}">
<!-- Template when active -->
</ControlTemplate>
<ControlTemplate x:Name="DeactivatedTemplate" TargetType="{x:Type MyType}">
<!-- Template when deactivated -->
</ControlTemplate>
<Style TargetType="{x:Type MyType}">
<Setter Property="Template" Value="{StaticResource DeactivatedTemplate}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsActive}" Value="True">
<Setter Property="Template" Value="{StaticResource ActiveTemplate}"/>
</DataTrigger>
</Style.Triggers>
</Style>
This assumes that MyType is a control that can has a ControlTemplate and that the DataContext has a Property IsActive to toggle the Template.

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}}" />

WPF: Once I set a property in code, it ignores XAML binding forever more... how do I prevent that?

I have a button that has a datatrigger that is used to disable the button if a certain property is not set to true:
<Button Name="ExtendButton" Click="ExtendButton_Click" Margin="0,0,0,8">
<Button.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="False">
<Setter Property="Button.IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
That's some very simple binding, and it works perfectly. I can set "IsConnected" true and false and true and false and true and false, and I love to see my button just auto-magically become disabled, then enabled, etc. etc.
However, in my Button_Click event... I want to:
Disable the button (by using ExtendButton.IsEnabled = false;)
Run some asynchronous code (that hits a server... takes about 1 second).
Re-enable the button (by using ExtendButton.IsEnabled = true;)
The problem is, the very instant that I manually set IsEnabled to either true or false... my XAML binding will never fire again. This makes me very sad :(
I wish that IsEnabled was tri-state... and that true meant true, false meant false and null meant inherit. But that is not the case, so what do I do?
Benny's answer provides one approach (and an excellent one). Another is to extend your model to support the "in async operation" state and to add another trigger to respond to that:
XAML:
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="False">
<Setter Property="Button.IsEnabled" Value="False" />
</DataTrigger>
<DataTrigger Binding="{Binding IsInAsyncOperation}" Value="True">
<Setter Property="Button.IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
Code-behind:
private void ExtendButton_Click(...)
{
IsInAsyncOperation = true;
// begin async operation
}
private void OnAsyncOperationComplete(...)
{
// retrieve results etc.
IsInAsyncOperation = false;
}
Note: You'd define the IsInAsyncOperation property on the same class as IsConnected; if that's a view model rather than the Window class, then you'll need to tweak the code-behind accordingly.
There's a much better way to get this functionality in WPF. It's the commanding system, and it's awesome. Any button, menu item, hot key, etc can be linked to a single command which automatically handles enabling/disabling (as you desire in your program). It's also clean and reusable, and sooooo easy to use.
For example, in an application of mine, I have an "about dialog" that shows up when the user hits F1. I created a command called AboutCommand by implementing ICommand.
public class AboutCommand:System.Windows.Input.ICommand
{
public bool CanExecute(object parameter)
{
return true; // in this case the command is never disabled
}
public event EventHandler CanExecuteChanged
{
// not needed in this case, but in commands when CanExecute actually
// changes, this performs the "magic" of disabling/enabling your controls
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter)
{
new AboutWindow().ShowDialog(); //happens when the command is executed
}
}
Then, in my window's xaml, I added*:
<Window.Resources>
<local:AboutCommand x:Key="About"/>
</Window.Resources>
<Window.InputBindings>
<KeyBinding Command="{StaticResource About}" Gesture="F1"/>
</Window.InputBindings>
You could also set the command to a button like so.
<Button Command="{StaticResource About}" Content="About this program"/>
Both the F1 key and the button would be disabled if AboutCommand.CanExecute() returned false.
*(I actually did it differently, because I'm using the MVVM pattern, but this works if you aren't using that pattern.)

Resources