WPF ItemsControl.ItemsTemplate Code Behind - wpf

I am currently using an ItemsControl template that binds to the the ViewModel to present a collection of objects. I have a ToggleButton as part of the template. I would like to access the object that is bound to that UI item in the collection in the code behind.
Here is the code that I have in place:
<ItemsControl.ItemTemplate>
<DataTemplate>
<StackPanel HorizontalAlignment="Stretch" Orientation="Horizontal">
<ToggleButton Cursor="Hand"
IsChecked="{Binding IsActive, Mode=TwoWay}"
IsEnabled="{Binding CanToggleOnProfile}"
Style="{StaticResource ProfileToggleButtonStyle}"
PreviewMouseLeftButtonUp="OnProfileToggle">
I would like to in my code behind on the OnProfileToggle call, access that particular object in the DataTemplate and do some stuff with it, but I can't seem to figure out how to access it (what index it is at in the collection, etc).

You will find your particular object in the DataContext of the sender:
private void OnProfileToggle(object sender, MouseButtonEventArgs e)
{
ToggleButton button = sender as ToggleButton;
object yourItem = button.DataContext;
}
Of course you have to cast yourItem to your item class.

Related

Replace a usercontrol by another one in WPF MaterialDesigntoolkit

I would like to be able to click on a Button named "More" then I will change the current UserControl with another detailed one in the MainWindow.
I tried to make the following but the items in leftmenubar don't work anymore when I run the last line of the code below:
private void btnMore_Click(object sender, RoutedEventArgs e)
{
CarDetailsViewModel c = new CarDetailsViewModel();
(this.Parent as ContentControl).Content = new CarDetails { DataContext = c };
}
I am using http://materialdesigninxaml.net/
The best way to do this would be like so:
<ContentControl>
<ContentControl.ContentTemplate>
<DataTemplate>
<local:CarDetails />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
Code behind:
private void btnMore_Click(object sender, RoutedEventArgs e)
{
CarDetailsViewModel c = new CarDetailsViewModel();
(this.Parent as ContentControl).Content = c;
}
Ideally, the car details viewmodel should be a property of a parent viewmodel bound to the content control's Content:
<ContentControl
Content="{Binding SelectedCarDetails}"
>
<ContentControl.ContentTemplate>
<DataTemplate>
<local:CarDetails />
</DataTemplate>
</ContentControl.ContentTemplate>
</ContentControl>
SelectedCarDetails would be updated by the parent viewmodel, and/or by selection in a ListBox or ListView, or whatever.
If you want to replace with a usercontrol of a different type, consider using implicit data templates:
<ContentControl>
<ContentControl.Resources>
<DataTemplate DataType="{x:Type models:CarDetailsViewModel}">
<local:CarDetails />
</DataTemplate>
<DataTemplate DataType="{x:Type models:BikeDetailsViewModel}">
<local:BikeDetails />
</DataTemplate>
<DataTemplate DataType="{x:Type models:BoatDetailsViewModel}">
<local:BoatDetails />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
Those could be defined in the UserControl's Resources, or in the Resources of any parent/grandparent/etc. element in the same XAML file. If you give it a Content of any of those viewmodel types, it'll automagically use the corresponding datatemplate.

User Control is not updating in window (WPF)

There is a UserControl which contains the bindings like below.
<TextBox Margin="5" Padding="0" IsReadOnly="True" Background="Transparent" BorderThickness="0" TextWrapping="Wrap" IsTabStop="False" FontSize="{DynamicResource TitleFontSize}" Text="{Binding ErrorTitle, Mode=OneWay}" />
it is bound by stack panel with name of GenericErrorControl and binding is as
<Visibility="{Binding IsShown, Mode=OneWay, Converter={StaticResource BoolToVis}, FallbackValue=Collapsed}">
Above control is added to one of view as below.
<views:GenericErrorControl Grid.Row="8" DataContext="{Binding GenericErrorControl, Mode=OneWay}" VerticalAlignment="Top/>
The problem is that user control is not appearing in the window after. In my viewmodel creating object i'm setting the value of IsShown but its not appearing. Kindly help and let me know if any other details needed.
It means that the binding is failing ie the fallback value is used from above code.
Things to do :
Ensure that your ViewModel inherits from the base class BindableBase ie it somehow implements the INotifyPropertyChanged interface and on property change, the PropertyChanged event is fired.
ie You have something similar to this in your view model.
private bool _IsShown;
public bool IsShown
{
get { return _IsShown; }
set { SetProperty(ref _IsShown, value); }
}
Double check your converter or post code here.

Binding to a viewmodel property in a DataTemplate

I'm fairly new to XAML but enjoying learning it. The thing I'm really struggling with is binding a property to an element in a DataTemplate.
I have created a simple WPF example to, (hopefully,) explain my problem.
I this example I am trying to bind the Visibility property of a CheckBox in a DataTemplate to a Property in my viewmodel. (Using this scenario purely for learning/demo.)
I have a simple DataModel named Item, but is of little relevance in this example.
class Item : INotifyPropertyChanged
{
// Fields...
private bool _IsRequired;
private string _ItemName;
And a fairly simple View Model named ItemViewModel.
class ItemViewModel : INotifyPropertyChanged
{
private ObservableCollection<Item> _Items;
private bool _IsCheckBoxChecked;
private bool _IsCheckBoxVisible;
public ObservableCollection<Item> Items
{
get { return _Items; }
set { _Items = value; }
}
public bool IsCheckBoxChecked
{
get { return _IsCheckBoxChecked; }
set
{
if (_IsCheckBoxChecked == value)
return;
_IsCheckBoxChecked = value;
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxChecked"));
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
}
}
public bool IsCheckBoxVisible
{
get { return !_IsCheckBoxChecked; }
set
{
if (_IsCheckBoxVisible == value)
return;
_IsCheckBoxVisible = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("IsCheckBoxVisible"));
}
(Constructors and INotifyPropertyChanged implementation omitted for brevity.)
Controls laid out in MainPage.xaml as follows.
<Window.Resources>
<local:VisibilityConverter x:Key="VisibilityConverter"/>
</Window.Resources>
<Window.DataContext>
<local:ItemViewModel/>
</Window.DataContext>
<Grid>
<StackPanel>
<CheckBox x:Name="checkBox" Content="Hide CheckBoxes" FontSize="14" IsChecked="{Binding IsCheckBoxChecked, Mode=TwoWay}" />
<ListView ItemsSource="{Binding Items}" HorizontalContentAlignment="Stretch" >
<ListView.ItemTemplate >
<DataTemplate>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<TextBlock Text="{Binding ItemName}"/>
<CheckBox Grid.Column="1" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" >
<CheckBox.DataContext>
<local:ItemViewModel/>
</CheckBox.DataContext>
</CheckBox>
</Grid>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<StackPanel Orientation="Horizontal" Margin="4,4,0,0">
<TextBlock Text="IsCheckBoxVisible:"/>
<TextBlock Text="{Binding IsCheckBoxVisible}" Margin="4,0,0,0" FontWeight="Bold" />
</StackPanel >
<Button Content="Button" Visibility="{Binding IsCheckBoxVisible, Converter={StaticResource VisibilityConverter}}" Margin="4,4,4,4"/>
</StackPanel>
</Grid>
The 'Hide CheckBoxes' checkbox is bound to IsCheckBoxChecked and is used to update IsCheckBoxVisible. I've also added a couple of extra controls below the DataTemplate to prove, (to myself,) the everything works.)
I have also implemented Jeff Wilcox's value converter. (Thank you.) http://www.jeff.wilcox.name/2008/07/visibility-type-converter/
When I run the app, checking and unchecking the 'Hide Checkbox', controls outside the DataTemplate function as expected but, alas, the Checkbox inside the data template remains unchanged.
I have had success with:
IsVisible="{Binding IsChecked, Converter={StaticResource VisibilityConverter}, ElementName=checkBox}"
But I'm not just trying mimic another control but make decisions based on a value.
I would REALLY appreciate any help or advice you can offer.
Thank you.
When you are in a DataTemplate, your DataContext is the data templated object, in this case an Item. Thus, the DataContext of the CheckBox in the DataTemplate is an Item, not your ItemViewModel. You can see this by your <TextBlock Text="{Binding ItemName}"/>, which binds to a property on the Item class. The Binding to IsCheckBoxVisible is trying to find a property called IsCheckBoxVisible on Item.
There are a couple of ways around this, but by far the easiest is to do this:
On your Window (in the xaml), give it and x:Name. Eg:
<Window [...blah blah...]
x:Name="MyWindow">
Change your binding to look like this:
<CheckBox Grid.Column="1"
Visibility="{Binding DataContext.IsCheckBoxVisible, ElementName=MyWindow, Converter={StaticResource VisibilityConverter}}">
We're using the Window as the source for the Binding, then looking at its DataContext property (which should be your ItemViewModel, and then pulling off the IsCheckBoxVisible property.
Another option, if you want something fancier, is to use a proxy object to reference your DataContext. See this article on DataContextProxy.

Silverlight 4 and LisBox's SelectedItem when using DataTemplate as ItemTemplate

I'm using tow listboxes for drag n drop where user can drag items from source listbox and drop on the target. I'm using a DataTemplate for the ListBoxItems of my ListBox Controls.
I need to give the user ability to move up/down items in the target listbox after they been moved from source. I have two button "Move Up" & "Move Down" to do that but when the user clicks on one of the button, it returns me the null object as selectedItem.
here is my code
private void moveUp_Click(object sender, RoutedEventArgs e)
{
ListBoxItem selectedItem = lstmenuItems.SelectedItem as ListBoxItem;
if (selectedItem != null)
{
int index = lstmenuItems.Items.IndexOf(selectedItem);
if (index != 0)
{
lstmenuItems.Items.RemoveAt(index);
index -= 1;
lstmenuItems.Items.Insert(index, selectedItem);
lstmenuItems.SelectedIndex = index;
}
}
}
I'm sure its to do with the ItemTemplate. here is the xaml for listbox
<ListBox x:Name="lstmenuItems" Height="300" MinWidth="200" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Code}"/>
<TextBlock Text="{Binding RetailPrice, StringFormat=£\{0:n\}}" />
</StackPanel>
<!-- Product Title-->
<TextBlock Text="{Binding Description1}" Width="100" Margin="2" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
Any ideas how can I access the selected Item and how can I move it up/down?
Thanks in advance
The selectedItem variable will contain a null because the SelectedItem property doesn't return the type ListBoxItem. The SelectedItem property returns an object it received from the collection supply its ItemsSource property.
Change to :-
object selectedItem = lstmenuItems.SelectedItem;
and that should get you a little further.
That said consider having the ItemsSource bound to an ObservableCollection and manipulate the collection instead.

ToggleButton group: Ensuring one item is always selected in a ListBox

I am trying to duplicate the left/center/right alignment toolbar buttons in Word. When you click the "Left Alignment" button the Center and Right buttons uncheck. I am using a WPF ListBox with ToggleButtons.
The problem is the user can click the Left Alignment button twice. The second click causes the button to uncheck and sets the underlying value to null. I'd like the second click to do nothing.
Ideas? Force the ListBox to always have one selected item? Prevent the null in the view model (need to refresh the ToggleButton binding)?
<ListBox ItemsSource="{x:Static domain:FieldAlignment.All}" SelectedValue="{Binding Focused.FieldAlignment}">
<ListBox.ItemTemplate>
<DataTemplate>
<ToggleButton IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}">
<TextBlock Text="{Binding Description}" />
</ToggleButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
yeah, i would prefer the radiobutton too for this case, but if you want to use togglebutton then maybe you can bind the isenabled property to ischecked so it cannot be cliked when it's checked
create custom control from ToggleButton,
in *.xaml.cs file, declare and define control
public class ToggleButton2 : ToggleButton
{
public bool IsNotCheckable
{
get { return (bool)GetValue(IsNotCheckableProperty); }
set { SetValue(IsNotCheckableProperty, value); }
}
// Using a DependencyProperty as the backing store for IsNotCheckable. This enables animation, styling, binding, etc...
public static readonly DependencyProperty IsNotCheckableProperty =
DependencyProperty.Register("IsNotCheckable", typeof(bool), typeof(ToggleButton2), new FrameworkPropertyMetadata((object)false));
protected override void OnToggle()
{
if(!IsNotCheckable)
{
base.OnToggle();
}
}
}
in *.xaml, replace ToggleButton with my:ToggleButton2, then you can bind IsNotCheckable to IsChecked, just like below,
<my:ToggleButton2 IsChecked="{Binding RelativeSource={RelativeSource Mode=FindAncestor,AncestorType={x:Type ListBoxItem}},Path=IsSelected}" IsNotCheckable="{Binding RelativeSource={RelativeSource Self}, Path=IsChecked, Mode=OneWay}">
Rather than implementing this as ToggleButtons, I would use RadioButtons with custom templates. It would probably save you a lot of headache.

Resources