Using the selected tab in a TabControl to clear a variable - WPF - wpf

I have a window which contains a a TabControl with 2 TabItems inside it. Inside the second tab, there is a TextBox with a binding to a string property inside my view model. I want to be able to clear this property (or the TextBox since it is bound it will will by association, clear the property) whenever the user clicks on a different tab (and also if the user closes the window). I'm hoping I can achieve this with only the XAML since I am trying to follow the MVVM pattern (so no code behind and my view model shouldn't know of the TabControl or TextBox that is in my view).
I have tried a few different things and can't quite get it working and while I'm getting better at WPF, I'm still fairly new to it. Inside my tabs I also have ListViews and ComboBoxes, which from my understanding will bubble the SelectionChanged event (same as when the tab selection is changed) so I should probably stay away from that. Any help would be appreciated.
Here was one of my attempts (snippet showing the gist):
<TabControl>
<TabItem>
...Stuff in first tab goes here
</TabItem>
<TabItem Header="Rules">
<TabItem.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{IsSelected}" Value="True">
<Setter TargetName="UserBox" Property="Text" Value="" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabItem.Style>
<GroupBox>
<UniformGrid>
<ListView ItemsSource="{Binding Source={StaticResource rulesViewModel}, Path=RulesList}">
<ListView.View>
<GridView>
<GridViewColumn Header="Rules"/>
</GridView>
</ListView.View>
</ListView>
<DockPanel>
<TextBox Name="UserBox" Text="{Binding Source={StaticResource rulesViewModel}, Path=User}" />
</DockPanel>
</UniformGrid>
</GroupBox>
</TabItem>
I'm pretty sure the line that reads Binding="{IsSelected}" is wrong. But I'm not sure how to target the correct property. Any ideas?

You need to set the SelectedIndex = -1 of the tab whose content you want to clear on other tabs click.
SelectedIndex = -1 is of the default tab so basically we are making this tab as the default one. Now the condition becomes that
if SelectedIndex ! = -1, text should be cleared
or at application shutdown, text should be cleared
I've provided you with the logic, code can now be written :)

I think the best way would be to use the interactivity library to catch the SelectionChanged property and change the property. It would look something like this:
<TabControl>
<i:Interaction.Triggers>
<i:EventTrigger EventName="SelectionChanged">
<ei:ChangePropertyAction TargetObject="{Binding Path=Path.To.ViewModel}" PropertyName="MyProperty" Value="" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TabControl>
You can also choose to change the value of the TextBox - in which case you'd need to modify the ChangePropertyAction object to look like this:
<ei:ChangePropertyAction TargetName="UserBox" PropertyName="Text" Value="" />

Related

WPF MVVM use Datatrigger to press button and perform a command

I'm super new to WPF and MVVM, and I need to execute a command commandToExec in my View as soon as my ViewModel sets a property propertyToSet to true. Right now, I have a Stackpanel that becomes visible when propertyToSet is set, and a button inside that Stackpanel that is bound to the command commandToExec, and a Datatrigger on that button that presses it if propertyToSet is set. Something that looks roughly like this:
<StackPanel Visibility="{Binding propertyToSet, Converter={StaticResource BooleanToVisibilityConverter}}">
<Button
Command="{Binding commandToExec}">
<Style TargetType="Button">
<Style.Triggers>
<DataTrigger Binding="{Binding commandToExec, Converter={StaticResource BooleanToVisibilityConverter}}">
<Setter Property="Button.IsPressed" Value="True" /> //Trying to set button isPressed to true to trigger command
</DataTrigger>
</Style.Triggers>
</Style>
</Button>
</StackPanel>
This doesn't work, and seems unintuitive and clunky, so I'm sure there is a better way to do this. I don't know much about XAML, so I'm just ballparking this code, but I would like to do something like this:
<StackPanel Visibility="{Binding propertyToSet, Converter={StaticResource BooleanToVisibilityConverter}}">
<SomethingThatAutomaticallyDoesCommand Command="{Binding commandToExec}"/>
<StackPanel/>
So that as soon as StackPanel becomes visible via propertyToSet, the arbitrary object SomethingThatAutomaticallyDoesCommand does its job and runs the command.
Another hacky idea I had saw to just set a tag on the StackPanel that updates it based on propertyToSet, and somehow binding my command to TargetUpdated, as found [here][1].
Anyway, I hope that made sense. Thanks so much!
[1]: Can i call a function via DataTrigger (XAML)?

How to handle Validations on Custom Control

I recently wrote my first custom control, an autocomplete textbox. Its Controltemplate consists of something like this
<Grid >
<Border/>
<TextBlock x:Name="Label"/>
<TextBox x:Name="TextLabel"/>
</Grid>
The first Textblock is used as a Label, the second one shows the content. The TextLabel binds on an Object, let's call it Customer If the underlying search doesn't find a Customer object, I want to show the errortemplate. When defining the TextLabel like this
<TextBox x:Name="PART_Editor"
HorizontalAlignment="Stretch"
VerticalAlignment="Center"
Validation.ErrorTemplate="{DynamicResource ValidationErrorTemplate}"
Style="{StaticResource TransparentTextBoxStyle}"
Text="{Binding Path=Text, RelativeSource={RelativeSource Mode=TemplatedParent},
Mode=TwoWay, ValidatesOnNotifyDataErrors=True,
NotifyOnValidationError=True,
ValidatesOnDataErrors=True,
UpdateSourceTrigger=PropertyChanged}" >
</TextBox>
the Validation is made and the error template of the textbox is shown. Unfortunately the Red Border is only around the inner TextBox and not around the whole custom control which looks just not good.
I was wondering how to achieve two things:
How can the ErrorTemplate of the CustomControl be triggered when one of the child-validations fail?
Do I have to specify all these NotifyOnValidationerror properties or is it possible to catch all errors on entity level and show the same ErrorTemplate
If you need additional Information, please just ask

Track where I clicked in ListView MVVM

I am writing a folder browser and I want to open folders on double click.
My folders are binded to ListView with GridView inside and I am tracking double click like this:
<i:EventTrigger EventName="MouseDoubleClick">
<Custom:EventToCommand Command="{Binding FolderOpenedCommand, Mode=OneWay}" CommandParameter="{Binding SelectedItem, ElementName=FolderView}"/>
</i:EventTrigger>
But I have an annoying issue: if I double click on gridview splitter to autosize column, it will also open selected folder, which I am not want to.
So, I have several options for now: put event handler inside style and use it with code behind or leave it as is, but in my case I want to do implement it with MVVM scenario because codebehind is not suitable for me.
My question is: how I can send my parameter as SelectedItem only if I click on the item and null when I click on something else?
I want to track this to make a proper behavior as far as I cannot apply double click to gridview on some reason.
Could someone please help me with this problem?
EDIT:
Lets clarify one thing to be sure we speak about the same things:
I can define something like this
<Style x:Key="itemStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="FolderView_OnMouseDoubleClick"></EventSetter>
</Style>
Bu I cannot do like this:
<Style x:Key="itemStyle" TargetType="{x:Type ListViewItem}" BasedOn="{StaticResource MetroListViewItem}">
<EventSetter Event="MouseDoubleClick" Handler="{Binding OpenFilesCommand}"></EventSetter>
</Style>
Because It will lead to exception. Now I want to understand how I can apply command here if Handler does not accept command? Do I need to write some attached property?
Avoid using listview. Use DataGrid instead. Then you can add eventtrigger to row style. ListView is obsolete class introduced in wpf 3 that was replaced by datagrid in wpf 4 an there no reason to use it anymore.
Another option is to use use custom behaviour implemented as attached property, e.g. InvokeCommandOnRowDoubleClick attached to Grid. To learn more about attached behaviours read this: http://blogs.msdn.com/b/dgartner/archive/2009/11/11/wpf-attached-behavior-example-watermark-text.aspx
There is a MouseDoubleClick property on the ListViewItem control. You can rework your style to contain a correct event when double clicking an item, apply it only to the ListViewItems and it won't listen to that event when not double clicking the gridview splitter.
You can read more about this here.
Well, to solve this issue I had to use InputBindings property for each control in each gridview column. I put Grid over controls and made like this:
<GridViewColumn Header="Size (Bytes)">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.InputBindings>
<MouseBinding Gesture="LeftDoubleClick" Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.FolderOpenedCommand}"
CommandParameter="{Binding ElementName=FolderView, Path=SelectedItem}"></MouseBinding>
</Grid.InputBindings>
<TextBlock Text="{Binding Path=Size, StringFormat='{}{0:#,#.}'}"/>
</Grid>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Unfortunately for my case I didnt find better solution

WPF MVVM - Binding highlighted item in Combobox

Is there a way to bind a highlighted ComboBox item in WPF/MVVM?
The reason I want this behavior is that I want to be able to right-click on a ComboBox item and pass the argument (Item Id for that matter) in the CommandParameter.
Since no item is actually selected until I left-click on the item, I have no args to pass...
Thoughts please?
<ComboBox.ContextMenu>
<ContextMenu>
<MenuItem Header="Delete Expense Category" Command="{Binding DeleteMenuItemCommand}"
CommandTarget="{Binding HighlightedExpenseCategory}" CommandParameter="{Binding Id}"></MenuItem>
</ContextMenu>
</ComboBox.ContextMenu>
First a question: what is your ultimate goal and can you approach it without having the user right click on a context menu item? Perhaps what you may want is a hierarchical combobox like in the image here Any ideas how to implement ComboBox to represent hierarchical data? . Though that may not suit you if you indeed just want to perform an action on an item in your combo box.
With that aside, then HighCore put you on the right track (sorry HighCore, I'm going to steal a little of your thunder here, but as a consolation I'm giving you a couple votes). The key thing is that the ContextMenu is a property of the ComboBoxItem and not the ComboBox itself.
This code snippet may help you get a step closer, though I haven't tested beyond XamlPad. You'll likely have an additional step of making sure your bindings can reach your view model with the command as well as your menu item's Id property, wherever you've defined that. Try the Binding's RelativeSource, but come back if you need help with that. Good luck!
<ComboBox>
<ComboBox.Resources>
<ContextMenu x:Key="contextMenu">
<MenuItem Header="Delete Expense Category" Command="{Binding DeleteMenuItemCommand}"
CommandTarget="{Binding HighlightedExpenseCategory}" CommandParameter="{Binding Id}" />
</ContextMenu>
</ComboBox.Resources>
<ComboBox.ItemContainerStyle>
<Style TargetType="{x:Type ComboBoxItem}">
<Style.Setters>
<Setter Property="ContextMenu" Value="{StaticResource contextMenu}" />
</Style.Setters>
</Style>
</ComboBox.ItemContainerStyle>
<ComboBoxItem Content="1" />
<ComboBoxItem Content="2" />
</ComboBox>

Strange behaviour in the WPF DataTemplate and property.

I have a DataTemplate that displays buttons in a StackPanel. Whenever a user clicks a button, the button is supposed to glow. So I have written the necessary DataTrigger in the Template and a Boolean condition in the property that I'm binding too. Here are the details below:
<DataTemplate x:Key="ActionItemsTemplate" DataType="ActionItemViewModel">
<ItemsControl IsTabStop="False" ItemsSource="{Binding}" Margin="6,2">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button x:Name="ActionButton" Command="{Binding Path=Command }" Content="{Binding Path=DisplayName}" Style="{DynamicResource HeaderButton}"/>
<!-- Set special values for Selected Item -->
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding IsSelected}" Value="True">
<Setter TargetName="ActionButton" Property="Style" Value="{DynamicResource MainWindowSelectedButton}"/>
<!--Command="{Binding Path=Command}" Content="{Binding Path=DisplayName}"-->
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
I have implemented the INotifyPropertyChanged interface and the Property ActionItems returns an ObservableCollection.
The Problem: When I change the ObservableCollection and call the INotifyPropertyChanged event, it is not reflected in the DataTemplate directly, but only altering the property. However if I re-assgn the entire object with itself, it works perfectly.
e.g
void Test1()
{
_commands[0].IsSelected = !_commands[0].IsSelected;
_commands[0] = _commands[0]; // Does not work If this line is commented out
ActionItems = _commands;
}
What could the problem be?
Edit: I see that the problem could be with the DataBinding in this case.
I have arrived at a similar problem now where I have bound the IsExpanded property of the Expander control to a bool property inside a TabPanel. When I toggle the bool property, the value is changed behind, but not reflected in the display. However suppose I change tabs and come back, I see the change has taken place. Is this related to this problem here?
And again I wonder what the problem could be (narrowed it down a bit :))
Edit 2: Solution for second Problem: I found that the OnPropertyChangedEvent of the INotifyPropertyChanged Interface needs to be called whenever a programmatic update on the
IsExpanded property is updated. As for the Original Problem, this does not seem to be the case, and I'm still trying to figure out what is going wrong. :)
I'm led to believe that you cannot replace the collection, only alter its contents.
a Boolean condition in the property that I'm binding too
I am not completely sure what you mean here, but I take the guess that this is the IsSelected property on your "commands" and that you forgot to raise PropertyChanged for that property.

Resources