Mainaining radio selection after item source change - wpf

Setup:
I have a combo-box, it's itemsource bound to an ObservableCollection<T> of a custom class, one property is a List<myenum>.
I have an itemscontrol which is databound to the combo-box's selected item List<myenum> property.
The itemscontrol datatemplate creates a list of radiobuttons, each representing the individual enum values in the list.
The Desire:
When I change the value in the combo-box the itemscontrol source is updated. What I want to occur, is if a radio button in the new itemscontrol source is the same as the selected radiobuton in the previous list (before it was updated), this to be checked.
Current Idea:
Asign a Checked event to the radio buttons, which maintains a myenum property in the window class which can be compared against. Make the IsChecked property of the radiobox bind to a converter and compare against the myenum property. To achieve this, I have made the window class extend from IValueConverter, this way the converter function has access to the myenum property.
Issue:
I don't know how to get the IsChecked binding to use the window as the converter. I have tried using relative source in the converter part of the binding, but that doesn't work
IsChecked="{Binding Converter={RelativeSource={RelativeSource Self}}}"
Preferred Answers:
Assistance on correcting the binding syntax if it's possible this way.
Ideas of a more appropriate way of achieving what I'd like.

I also do not know how to use the window as a value converter in xaml. Instead create a standalone value converter class with a public property for the enum type. Next, in the constructor of the window, get a reference to the instance of the value converter and store it in a private member.
XAML:
<local:MyValueConverter x:Key="ConvertSomething" />
Code behind:
private MyValueConverter _myValueConverter;
public Window1()
{
InitializeComponent();
_myValueConverter = FindResource("ConvertSomething") as MyValueConverter;
}
private void RadioButton_Checked(object sender, RoutedEventArgs e)
{
// You can access _myValueConverter here and set its public enum property.
}

Related

How to implement object selection for an itemscontrol through a click action

I am in desperate need of assistance.
My app has been created in WPF and on the screen are two itemscontrols that use the same observable collection as the itemssource. One has the elements laid out in a grid and the other has the elements laid out on a ellipse based on x-y variables in the list.
However, I am stuck when trying to implement the following:
I want to click on one of the datatemplates generated on either the ellipse or the grid to select it and have BOTH corresponding elements glow or do something else to indicate a selection. (i.e. If I click on the template in the grid, both the grid and the ellipse will do something to indicate that template has been selected.)
Right now, I have been able to save the item clicked on using this type of binding in the datatemplate. The itemsource in question is contained inside ItemsSourceViewModel.
<StackPanel.InputBindings>
<MouseBinding Command="{Binding SelectImage}" CommandParameter="{Binding Path=.}" MouseAction="LeftClick"></MouseBinding>
</StackPanel.InputBindings>
Then the SelectImage Icommand that is bound to the mouseclick is implemented as such:
private ItemsSourceViewModel foo;
public SelectImage(ItemsSourceViewModel incoming)
{
this.foo = incoming;
}
public event EventHandler CanExecuteChanged;
public bool CanExecute(object parameter)
{
return true;
}
public void Execute(object parameter)
{
var obj = parameter as ImageTemplate;
foo.SelectedImage = obj;
}
So now my selected object is saved inside my instance of ItemsSourceViewModel, but how do I go about determining which template in both itemscontrols have been selected and how do I apply the triggers so they show that they have been selected?
Please help! :'(
How About defining the input binding in your dataTemplate ItemTemplate. and maybe sending the name of your template as a parameter, then you could switch on the name of your template to do different handling?

Bind window model value to User Control Dependency Property

I have a simple user control that has One Dependency Property (the control is the model of itself)
The property is not directly bound to anything inside the user control, but I need to Bind its value to the Model of the window (or user control or whatever) where I put my user control.
If I set manually the User control Property Value, the property is modified correctly so I can assume the dependency property in the user control is working.
If I set the value to the Property binding it to my window model like this
<lctrl:InfoIconControl Grid.Row="0" Name="InfoIconTest" IconType="{Binding Path=IconTypeValue}"/>
Where IconTypeValue is a property of the window model, when I set the value of the window model property it does not change inside my user control. I presume I did something wrong but at the moment I have no clue.
Two possibilties come to mind as likely:
Your "model" (you mean viewmodel?) does not implement INotifyPropertyChanged and/or you're not firing the PropertyChanged when IconTypeValue changes its value.
You've done something like this.DataContext = this inside your UserControl and now the Binding is not working because it is looking for the IconTypeValue property inside your control, instead of looking for it in the "model".
Solution to option 1 is easy: implement the interface and make sure you fire the event when the property changes.
Solution to option 2 is simply removing any setting of DataContext inside your UserControl, and instead rely on relative Bindings (RelativeSource, ElementName, etc.) in your control's XAML. Or if you gotta set the DataContext of something, do NOT set the UserControl's one. Instead, set the DataContext of a container INSIDE the UserControl.
In your case, since you're using a viewmodel for your UserControl, using it as DataContext makes sense. But if you wanna support binding to the DependencyProperties of your UserControl, you're then gonna have to set your viewmodel as DataContext of something else... For instance, the first Grid in your XAML.
Just name the Grid:
<Grid x:Name="LayoutRoot">
And set your viewmodel as its DataContext:
InfoIconControlModel mModel;
public InfoIconControl()
{
InitializeComponent();
mModel = new InfoIconControlModel();
LayoutRoot.DataContext = mModel; // this.DataContext = mModel; <-- DON'T DO THIS
}
After that, the Bindings will begin to work. But you've made another typical mistake: you're only calling SetIcon from the CLR setter of your propertty.
public InfoIconType IconType
{
get
{
return (InfoIconType)this.GetValue(IconTypeProperty);
}
set
{
this.SetValue(IconTypeProperty, value);
this.SetIcon(); // <-- This won't work with Binding
}
}
Instead, you must also call it from the DependencyPropertyChanged callback (that you had already defined, on the other hand):
/// <summary>
/// Icon Type dependency Property
/// </summary>
public static readonly DependencyProperty IconTypeProperty = DependencyProperty.Register(
FLD_IconType, typeof(InfoIconType), typeof(InfoIconControl), new PropertyMetadata(InfoIconType.ICPlus, IconTypePropertyChanged));
///<summary>
///
///</summary>
private static void IconTypePropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
InfoIconControl ic = sender as InfoIconControl;
ic.SetIcon(); // <-- This will work with Binding
}

Recommend best approach in this situation

I'm experimenting with MVVM and I can't quite wrap my mind around it.
I have a View (windows) that has several repeated controls.
Let's say I have 4 textbox - button pairs. Their behavior should be the same, after pressing a button paired textbox says "Hello World!"
I have tried several options I could think of:
One ViewModel, 4 string properties binded to textboxes, 1 command
When I bind each button to the same command I can't tell which property needs to be set.
Passing enum to CommandParameter feels awkward.
One ViewModel and UserControl that hosts a textbox and a button repeated 4 times.
Now I need to expose all the properties like Command, CommandParameter, Text etc.. Seems like a lot of work.
Still can't say which property needs to be updated after click.
Each UserControl has a ViewModel
This solves button clicking and setting property, but now I have no clue how to extract texts from nested ViewModels to the window ViewModel.
Is there any other way? I suspect DataTemplates could be of use, but I'm not sure how.
What you describe is such an abstract and contrived idea that it doesn't warrant MVVM. You're talking about TextBoxes and Buttons, which are all 'View', and not the MVVM way of thinking. You'd almost always start with a Model.
There is no 'Model' per-se here though; your specification is literally to set the value of a TextBox on a Button click. The seemingly random list of '4' items (picked out of nowhere) and a seemingly useless TextBox mean nothing.
Putting that aside and assuming that you have a set of 4 business entities, each with a field on them that is user-editable, and an action that the user can trigger, you'd do this:
Create a ViewModel class to represent an item - eg MyItemModel
Create a ViewModel class to represent the set of items (likely it will just return a Collection of the first) - eg AllMyItemsListModel
Then for the View:
Create an ItemsControl, with ItemsSource bound to an instance of the 'collection' of the second ViewModel class
For each ItemTemplate, have a template or UserControl for each item
Within the template or UserControl, bind the TextBox's Text property to the appropriate property of the first class
Bind the Button's Command property to a property on the first class returning an ICommand - using RelayCommand for example
I don't know what you mean about 'extracting texts from nested ViewModels to the window ViewModel' - what does that mean and why would you want to do it?
Hope that helps.
Number 3. Except there would just be one UserControl with viewmodel and then the Main page that would have multiple instances of that UserControl on it. If the main window needs info from the UserControl you could pass it through events or use something like MVVM Light and it's messenger class.
There's no need to create a separate ViewModel for a reusable control that has such simple behavior. Just by adding a few DependencyProperties and an event handler to the simple UserControl you can reuse the logic and only set the properties that are actually different on each instance. For the UserControl XAML you just need to hook up the TextBox to the DependencyProperty and the Button to a Click handler.
<DockPanel>
<Button Content="Reset" Click="Button_Click"/>
<TextBox Text="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type UserControl}}, Path=Text}"/>
</DockPanel>
The code for the UserControl just needs to define the properties that can be bound externally and the handler to reset the Text.
public partial class ResetTextBox : UserControl
{
public static readonly DependencyProperty TextProperty = DependencyProperty.Register(
"Text",
typeof(string),
typeof(ResetTextBox),
new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty ResetTextProperty = DependencyProperty.Register(
"ResetText",
typeof(string),
typeof(ResetTextBox),
new UIPropertyMetadata(String.Empty));
public string ResetText
{
get { return (string)GetValue(ResetTextProperty); }
set { SetValue(ResetTextProperty, value); }
}
public ResetTextBox()
{
InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
Text = ResetText;
}
}
Then to use the control you just need to bind to your ViewModel string properties and set the default text that should be applied on a reset which can either be hardcoded here, or bound to other VM properties.
<StackPanel>
<local:ResetTextBox ResetText="One" Text="{Binding Name1}"/>
<local:ResetTextBox ResetText="Two" Text="{Binding Name2}"/>
<local:ResetTextBox ResetText="Three" Text="{Binding Name3}"/>
</StackPanel>

WPF: Bind/Apply Filter on Boolean Property

I want to apply a filter to a ListBox accordingly to the IsSelected property of a CheckBox.
At the moment I have something like this.
XAML
<CheckBox Name="_filterCheckBox" Content="Filter list" Checked="ApplyFilterHandler"/>
<ListBox ItemsSource="{Binding SomeItems}" />
CodeBehind
public ObservableCollection<string> SomeItems { get; private set; }
private void ApplyFilterHandler(object sender, RoutedEventArgs e)
{
if (_filterCheckBox.IsChecked.Value)
CollectionViewSource.GetDefaultView(SomeItems).Filter += MyFilter;
else
CollectionViewSource.GetDefaultView(SomeItems).Filter -= MyFilter;
}
private bool MyFilter(object obj)
{
return ...
}
It works but this solution feels like the old-fashioned way (Windows Forms).
Question:
Is it possible to achieve this with Bindings / in XAML?
Thanks for your time.
The only way I can think of would be to make an ObjectDataProvider and two separate CollectionViewSource objects in XAML. One view would have the filter applied, the other would not. Then you could bind directly to the CheckBox.IsChecked property and use a custom IValueConverter. The value converter would have 2 dependency properties- both of type CollectionViewSource. These properties might be called, "UnfilteredItems" and "FilteredItems." In XAML, you would set the unfiltered items property to the CollectionViewSource that was unfiltered, and the filtered items property to the one with the filter. The converter logic itself would be simple- if true, return the filtered CollectionViewSource, if false, return the unfiltered one.
This solution is not extremely elegant, but it would get the job done. Because Filter is not a DependencyProperty and can only be specified by an event-handler, our hands are kind of tied on this one. I don't think your solution is a bad one, though.

WPF databinding IsEnabled Property

So I am learning WPF right now, and want to do a simple databind between a bool value, and whether or not a MenuItem is enabled or not.
I have coded it like this:
<MenuItem Name="miSaveFile" Header="Save" Click="miSaveFile_Click"
IsEnabled="{Binding}" />
And in the .cs file I set:
miSaveFile.DataContext = dataChanged;
For some reason the MenuItem doesn't seem to be properly reflecting the state of dataChanged.
What am I missing?
You are better off binding to an object than to a primitive type. This object is often referred to as the "model" for your view.
WPF uses the INotifyPropertyChanged interface for the model (or often view-model) to notify the view that the model has changed states.
So you will first want to define a data class as the model that implements the INotifyPropertyChanged interface and fires the PropertyChanged event whenever a property is changed.
When you set a binding, you have 5 main elements on the binding to worry about. The binding has a source object, a source path on the source object, a target object, a target property on the target object, and an optional converter.
If you do not specify the source, it defaults to the DataContext of the control the binding is set on. There are other options for setting the source. Here is a Microsoft article on setting the source. You can then set the path of a property to pull out of the source for the binding. In your case, the source is a boolean and there is no path because the binding is using the whole source object.
The target is always the control that you set the binding on, and the target property is the property on this control that you are binding to. In this case, MenuItem and IsEnabled.
A converter can optionally convert the source value into a value that is compatible with the target property. You can use any object for a converter that implements IValueConverter or IMultiValueConverter (for MutliBindings).
In your case, I would first create a model that implements INotifyPropertyChanged. Next, I would assign the DataContext of the menu to an instance of the model. Then I would set the binding to:
IsEnabled="{Binding Path=EnableFlag}"
(Where EnableFlag is a boolean property in the model that you want to menu to bind to)
If you set up the INotifyPropertyChanged interface correctly, the menu item will be enabled/disabled whenever you change this property on the model.
For a MenuItem, would it not be a better approach to use the Command model rather than Click and IsEnabled properties?
After InitialiseComponent():
this.CommandBindings.Add(new CommandBinding(ApplicationCommands.Save, fileSaveExecuted, fileSaveCanExecute));
Additional methods:
/* here is where you set e.CanExecute true for enabled: */
private void fileSaveCanExecute(object x, CanExecuteRoutedCommandEventArgs e)) { e.CanExecute = ...; e.Handled = true; }
/* here is where you act on the command: */
private void fileSaveExecuted(object sender, ExecutedRoutedEventArgs e) { ... }
XAML:
<MenuItem Header="_Save" Command="Save"/>
How does the UI know when the dataChanged variable has actually changed?
I normally bind to a property on an object, and let that class implement INotifyPropertyChanged. The UI is then "automagically" updated whenever the PropertyChanged event is invoked.
So I would have
<MenuItem Name="miSaveFile" Header="Save" Click="miSaveFile_Click"
IsEnabled="{Binding DataChanged}"</MenuItem>
and then set miSaveFile.DataContext = myObject.DataChanged
(myObject can be this if you are using the codebehind)
Edit: I just made a quick test. If you set the data context directly to the DataChanged property, an subscription to the PropertyChanged event on the owner object is not added. But the solution I suggest works.

Resources