MVVM-How to Binding ObservableCollection of Strings into ListBox WPF - c#

Normally I bind ObservableCollection to my own classes. But in this case I need to bind ObservableCollection of Strings in ListBox using MVVM into WPF.
My xml is
<Window x:Class="ListBoxDynamic.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525"
xmlns:vm="clr-namespace:ListBoxDynamic.ViewModel">
<Grid Margin="0,0,-8,0">
<ListBox Width="100" Height="90" ItemsSource="{Binding ListOfItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<ToggleButton Command="{Binding SelectItemCommand}" CommandParameter="{Binding ElementName=Item}" >
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<TextBlock x:Name="Item" Text="{Binding What I must to write?}" />
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
My MainViewModel
private ObservableCollection<string> _listOfItems = new ObservableCollection<string>();
public ObservableCollection<string> ListOfItems
{
get { return _listOfItems; }
set { _listOfItems = value; RaisePropertyChanged("ListOfItems"); }
}
public ICommand SelectItemCommand { get; private set; }
int counter = 1;
public MainViewModel()
{
ListOfItems.Add("Add new Item");
SelectItemCommand = new RelayCommand<object>((x) => ExecuteSelectItemCommand(x));
}
But I don't know that I must to write in Binding TextBlock into ToggleButton.

Since data which back each ListBoxItem is string, hence DataContext of each ListBoxItem will be string.
Hence doing <TextBlock x:Name="Item" Text="{Binding }" /> will show you the string backing the listboxitem in the textblock

there are few issues I notices
the command is available in the view model instead of the data item, so use relative source to bind to command
"{Binding}" can be used to refer to current Item, so no need to use elementname syntax
then instead of placing a textbox inside toggle button you can make use of content presenter to make it more flexible
so this could go like this
<ListBox Width="100"
Height="90"
ItemsSource="{Binding ListOfItems}">
<ListBox.ItemTemplate>
<DataTemplate>
<ToggleButton Command="{Binding SelectItemCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=ListBox}}"
CommandParameter="{Binding}"
Content="{Binding}">
<ToggleButton.Template>
<ControlTemplate TargetType="ToggleButton">
<ContentPresenter />
</ControlTemplate>
</ToggleButton.Template>
</ToggleButton>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Related

Can't bind DataContext in element ResourceDictionary

In my UserConterl.
<UserControl.Resources >
<ResourceDictionary >
<ContextMenu x:Key="MyContextMenu " >
<MenuItem Header="{Binding Header}">
</ContextMenu >
<HierarchicalDataTemplate x:Key="MyTemplate">
<TextBlock x:Key="MyTextBlock" Text = {Binding Header} ContextMenu="{StaticResource MyContextMenu }"/>
</HierarchicalDataTemplate >
</ResourceDictionary >
</UserControl.Resources>
<TreeView ItemTemplate="{StaticResource MyTemplate}" ItemsSource="{Binding MySources}" >
above codes in xaml.There codes is in .cs of UserControl xaml.
public MyUserControl()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
public class MyViewModel: ViewModelBase
{
public string Header {get; set;}
public List<string> MySources \\ Has been assigned
}
The result expected is that display the field Header when I click rightbutton.Actually, the Popup Menu is empty.I found ContextMenu didn't bind DataContext. What should I do?
Thank!
Minus setting up your datacontext, if you are trying to bind context menu items to a parent control's datacontext, you have to use the PlacementTarget.Tag trick. This is because the context menu is on a different visual tree.
You also don't need the Header in <TextBlock Text="{Binding Header}", leave it as <TextBlock Text="{Binding}"
<Grid Background="DarkGray">
<Grid.Resources>
<ContextMenu x:Key="CM">
<MenuItem Header="{Binding PlacementTarget.Tag.Header,
RelativeSource={RelativeSource AncestorType=ContextMenu, Mode=FindAncestor}}"/>
</ContextMenu>
<HierarchicalDataTemplate x:Key="MyTemplate">
<TextBlock Text="{Binding}"
ContextMenu="{StaticResource CM }"
Tag="{Binding DataContext,RelativeSource={RelativeSource AncestorType=TreeView, Mode=FindAncestor}}"/>
</HierarchicalDataTemplate >
</Grid.Resources>
<Grid.RowDefinitions>
<RowDefinition/>
</Grid.RowDefinitions>
<TreeView Grid.Row="0"
ItemTemplate="{StaticResource MyTemplate}"
ItemsSource="{Binding Sources}"></TreeView>
</Grid>

BindingExpression Path Error - How to structure code in MVVM model?

I have a Button that when clicked should adjust the Widths of two Grids.
<DataTemplate x:Key="ItemTemplate">
<DockPanel Width="Auto">
<Button Click="SelectMovie_Click" DockPanel.Dock="Top">
<Button.Template>
<ControlTemplate >
<Image Source="{Binding image}"/>
</ControlTemplate>
</Button.Template>
<i:Interaction.Behaviors>
<local:UniqueNameBehavior ID="{Binding id}"/>
</i:Interaction.Behaviors>
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding ShowRightGridCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</Button>
<TextBlock Text="{Binding title}" HorizontalAlignment="Center" DockPanel.Dock="Bottom"/>
</DockPanel>
</DataTemplate>
This Button is displayed within a Grid as such:
<Grid Grid.Row="2" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="{Binding LeftGridWidth}" />
<ColumnDefinition Width="{Binding RightGridWidth}" />
</Grid.ColumnDefinitions>
// BUTTONS DISPLAYED HERE
<Grid x:Name="LeftGrid" Grid.Row="2" Grid.Column="0" >
<Border BorderThickness="1" BorderBrush="Red">
<ItemsControl ItemTemplate="{StaticResource ItemTemplate}" ItemsSource="{Binding _movies}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<UniformGrid Columns="5" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Border>
</Grid>
<Grid x:Name="RightGrid" HorizontalAlignment="Left" Grid.Row="2" Grid.Column="1" >
<DockPanel>
<StackPanel VerticalAlignment="Top" Height="200">
<TextBlock Width="200" Height="50" DockPanel.Dock="Top" HorizontalAlignment="Left">
<TextBlock.Text>
<MultiBinding StringFormat="{}{0} ({1})">
<Binding Path = "SelectedMovie.title"/>
<Binding Path = "SelectedMovie.year"/>
</MultiBinding>
</TextBlock.Text>
</TextBlock>
</StackPanel>
<StackPanel VerticalAlignment="Top" Height="200">
<Grid HorizontalAlignment="Center">
<Image Source="star_icon.png" Width="100" Height="100" VerticalAlignment="Top"/>
<TextBlock Text="{Binding SelectedMovie.rating}" Style="{StaticResource AnnotationStyle}" Width="150"/>
</Grid>
</StackPanel>
</DockPanel>
</Grid>
</Grid>
ViewModel
public List<MediaDetail> _movies { get; set; }
public string selectedMovieID { get; set; }
private GridLength _leftGridWidth;
private GridLength _rightGridWidth;
private readonly GridLength _defaultRightGridWidth = new GridLength(0, GridUnitType.Pixel);
public MoviePanelViewModel()
{
ShowRightGridCommand = new DelegateCommand(SwitchRightGridWidth);
LeftGridWidth = new GridLength(7, GridUnitType.Star);
RightGridWidth = _defaultRightGridWidth;
}
private void SwitchRightGridWidth()
{
RightGridWidth = RightGridWidth == _defaultRightGridWidth ? new GridLength(3, GridUnitType.Star) : _defaultRightGridWidth;
}
public GridLength LeftGridWidth
{
get { return _leftGridWidth; }
set { _leftGridWidth = value; OnPropertyChanged("LeftGridWidth"); }
}
public GridLength RightGridWidth
{
get { return _rightGridWidth; }
set { _rightGridWidth = value; OnPropertyChanged("RightGridWidth"); }
}
public ICommand ShowRightGridCommand { get; set; }
The problem is that when I run my code, I get the error:
BindingExpression path error: 'ShowRightGridCommand' property not found on 'object' ''MediaDetail'
I am not sure how to structure my code such that the Width properties for the Grids can remain in the ViewModel but the Trigger for my Button also works. The DataContext for my View is the ViewModel.
Is there a good way to achieve this?
Thank you for your help.
The problem is your binding inside a DataTemplate: WPF tries to find the property path ShowRightGridCommand on your MediaDetail class, because the ItemsControl sets the data context for each item container it generates to the corresponding item.
What you actually want to do is binding to the view model. You can do this like so:
{Binding ElementName=LeftGrid, Path=DataContext.ShowRightGridCommand}
LeftGrid is an element outside of the ItemsControl. Its DataContext is your MoviePanelViewModel instance, and there is your ShowRightGridCommand property defined.
A little cleaner would be to put the name root on your view's root control (usually a UserControl or Window), and then use ElementName=root,Path=DataContext.... as your binding - at least that's what I usually do.
An alternative would be to use RelativeSource={RelativeSource UserControl}, but I find ElementName=root to be simpler.

Binding RadioButton.IsChecked with Listbox.SelectedItem

My View is clickable like a radiobutton with about 7 other of these radiobuttons, but my listbox is not updating it's selected property unless I click outside of my radiobutton.
Basically I have a AbstractTask as my base. I have 7 child classes. I want to be able to select one of these AbstractTasks. That's all i'm after. So in my main window i have this.
<Window x:Class="AdvancedTaskAssigner.View.MainWindowView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:v="clr-namespace:AdvancedTaskAssigner.View"
Title="MainWindowView" Height="300" Width="300" SizeToContent="Height">
<DockPanel>
<TextBlock Text="TextBlock" DockPanel.Dock="Top" />
<TextBlock Text="{Binding ElementName=listTasks, Path=SelectedItem.Name}" DockPanel.Dock="Top" />
<ListBox x:Name="listTasks" ItemsSource="{Binding Tasks}" HorizontalContentAlignment="Stretch" SelectedItem="{Binding IsSelected}">
<ListBox.ItemTemplate>
<DataTemplate>
<v:AbstractTaskView />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</DockPanel>
</Window>
since this is a library and not a application I had to put this in the Constructor of MainWindowView
MainWindowView.xaml.cs
public MainWindowView()
{
InitializeComponent();
var atvm = new ViewModel.MainWindowViewModel();
atvm.LoadTasks();
this.DataContext = atvm;
}
MainWindowViewModel.cs
class MainWindowViewModel
{
internal void LoadTasks()
{
var assembly = Assembly.GetAssembly(typeof(AbstractTask)).GetTypes().Where(t => t.IsSubclassOf(typeof(AbstractTask)));
Type[] typelist = GetTypesInNamespace(Assembly.GetAssembly(typeof(AbstractTask)), typeof(AbstractTask));
foreach (Type t in typelist)
{
if(!t.IsAbstract && t.BaseType.Equals(typeof(AbstractTask)))
{
tasks.Add(new AbstractTaskViewModel(t));
}
}
}
private Type[] GetTypesInNamespace(Assembly assembly, Type baseClass)
{
return assembly.GetTypes().Where(t => t.IsSubclassOf(baseClass)).ToArray();
}
private ObservableCollection<AbstractTaskViewModel> tasks = new ObservableCollection<AbstractTaskViewModel>();
public ObservableCollection<AbstractTaskViewModel> Tasks
{
get { return tasks; }
}
}
AbstractTaskViewModel.cs
public class AbstractTaskViewModel
{
public AbstractTaskViewModel(Type task)
{
if (!task.IsSubclassOf(typeof(AbstractTask)))
{
throw new NotSupportedException(string.Format("{0} is not a subclass of AbstractTask", task.Name));
}
Task = task;
}
public string Description
{
get
{
return GetCustomAttribute(0);
}
}
public string Name
{
get
{
return GetCustomAttribute(1);
}
}
public bool IsSelected{get;set;}
private string GetCustomAttribute(int index)
{
var descriptions = (DescriptionAttribute[])Task.GetCustomAttributes(typeof(DescriptionAttribute), false);
if (descriptions.Length == 0)
{
return null;
}
return descriptions[index].Description;
}
protected readonly Type Task;
}
AbstractTaskView.xaml
<RadioButton
x:Class="AdvancedTaskAssigner.View.AbstractTaskView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" GroupName="AbstractTasks" Background="Transparent" IsChecked="{Binding IsSelected, Mode=TwoWay}">
<RadioButton.Template>
<ControlTemplate TargetType="{x:Type RadioButton}">
<Grid>
<Border x:Name="MyHead" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Background="LightGray" Margin="20,0,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Panel.ZIndex="2">
<TextBlock Text="{Binding Name}" Margin="5,2" MinWidth="50" />
</Border>
<Border x:Name="Myoooo" BorderBrush="Black" BorderThickness="2" CornerRadius="5" Background="Transparent" Margin="0,10,0,0" Panel.ZIndex="1">
<TextBlock Text="{Binding Description}" Margin="5,15,5,2" />
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter TargetName="MyHead" Property="Background" Value="LightGreen" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</RadioButton.Template>
</RadioButton>
this is what i am getting..
I want the green border to be the selected item. not what the Listbox's Selected item is. The bottom text box is the name of the selected item.
There are some issues related to binding IsChecked property of RadioButton. You can read about it here or here. You can apply the solution presented in the first link using your AbstractTaskView instead of RadioButton. Replace your listbox in MainWindowView with the code:
<ListBox x:Name="listTasks" ItemsSource="{Binding Tasks}" HorizontalContentAlignment="Stretch">
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<v:AbstractTaskView Content="{TemplateBinding Content}"
IsChecked="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=IsSelected}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
You have to also remove the folowing part of code from AbstractTaskView:
IsChecked="{Binding IsSelected, Mode=TwoWay}"
I hope you don't need IsSelected property of AbstractTaskViewModel to be set. But if you do, you can set it for example in Checked event handler in AbstractTaskView code behind (I know it's not pretty solution).
I see you're binding the IsSelected (boolean) to the SelectedItem (object)

wpf check list box

I m new to wpf.In order to get check list box functionality ,I have added below xaml to my code,but there is no output in my screen.only blank,what it could be?
<TabItem Header="Samples" >
<ListBox Margin="10" Width="373" Height="236">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="MyText"/>
<CheckBox IsChecked="False"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</TabItem>
just have a look at this basic sample
http://merill.net/2009/10/wpf-checked-listbox/
List box is a bit wired for such task..Have a look at ItemsControl.
Here is the code i use:
<ItemsControl
ItemsSource="{Binding ***}" IsTabStop="False">
<ItemsControl.ItemTemplate>
<DataTemplate>
<CheckBox
Content="{Binding Name}"
IsChecked="{Binding IsSelected}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Replace your code with this
<TabItem Header="Roles" >
<ListBox Margin="10" Width="373" Height="236">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="MyText"/>
<CheckBox IsChecked="False"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBoxItem>Hi</ListBoxItem>
</ListBox>
</TabItem>
and tell us if it still shows blank
Better still, just use the new CheckListBox control in the Extended WPF Toolkit
http://wpftoolkit.codeplex.com/wikipage?title=CheckListBox&referringTitle=Home
This might help
1.Inorder to work datatemplate you must specify itemsource, here i have bounded a Stateslist a collection of items into it.
2.Also set the Datacontext to ViewModel or the CodeBehind as datacontext.
3.Datacontext will distribute the StateList properties collection to the listbox itemsource
using codebehind -
public Window1()
{
InitializeComponent();
this.DataContext = this;
LoadData();
}
using viewmodel
public Window1()
{
InitializeComponent();
DataContext = new Window1ViewModel();
LoadData();
}
//MyItemsource Property for listbox
private ObservableCollection<States> _stateslist;
public ObservableCollection<States> StatesList
{
get { return _stateslist; }
set
{
_stateslist = value;
RaisePropertyChanged(() => StatesList);
}
}
// Sample Data Loading
public void LoadData()
{
StatesList = new ObservableCollection<States>();
StatesList.Add(new States
{
StateName = "Kerala"
});
StatesList.Add(new States
{
StateName = "Karnataka"
});
StatesList.Add(new States
{
StateName = "Goa"
});
}
Window1.Xaml
<ListBox ItemsSource="{Binding StatesList}" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<CheckBox IsChecked="{Binding IsSelected"} Content="{Binding StateName}" />
<TextBox Text="{Binding TextBoxValue}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Check this out it isworking..you are using TabItem but you didnt define it in TabControl
<TabControl>
<TabItem Header="Tab1">
<ListBox Margin="10" Width="373" Height="236">
<ListBox.Items>
<StackPanel Orientation="Horizontal">
<TextBlock Text="MyText"/>
<CheckBox IsChecked="False"/>
</StackPanel>
</ListBox.Items>
</ListBox>
</TabItem>
</TabControl>
If you are new in WPF use XamlPadX it will give you great help to practice out on it..

How to access base datacontext in Collection in xaml

I have a wpf app with the datacontext set to an instance of a viewmodel class. It all works fine except where I need to access a property of the viewmodel in a listbox with the datacontext set to a collection that is contained in the ViewModel class. On msdn it says you can escape using the \ character but that has not worked for me
My code
public class StatusBoardViewModel : INotifyPropertyChanged
{
OIConsoleDataContext db = new OIConsoleDataContext();
// the collection
private IQueryable<Issue> issues;
public IQueryable<Issue> Issues
{
get
{
// Lazy load issues if they have not been instantiated yet
if (issues == null)
QueryIssues(); // This just runs a linq query to set the property
return issues;
}
set
{
if (issues != value)
{
issues = value;
OnPropertyChanged("Issues");
}
}
}
// The property I need to access
private bool showDetailListItems = true;
public bool ShowDetailListItems
{
get
{
return showDetailListItems;
}
set
{
if (showDetailListItems != value)
{
showDetailListItems = value;
OnPropertyChanged("ShowDetailListItems");
}
}
}
}
in the window1.xaml.cs
//instantiate the view model
StatusBoardViewModel statusBoardViewModel = new StatusBoardViewModel();
public Window1()
{
InitializeComponent();
// setting the datacontext
this.DataContext = statusBoardViewModel;
}
And the Xaml
// This is in the Window1.xaml file
<ListBox x:Name="IssueListBox"
ItemsSource="{Binding Issues}" // Binds the listbox to the collection in the ViewModel
ItemTemplate="{StaticResource ShowIssueDetail}"
IsSynchronizedWithCurrentItem="True"
HorizontalContentAlignment="Stretch" BorderThickness="3"
DockPanel.Dock="Top" VerticalContentAlignment="Stretch"
Margin="2" MinHeight="50" />
// The datatemplate from the app.xaml file
<DataTemplate x:Key="ShowIssueDetail">
<Border CornerRadius="3" Margin="2" MinWidth="400" BorderThickness="2"
BorderBrush="{Binding Path=IssUrgency, Converter={StaticResource IntToRYGBBoarderBrushConverter}}">
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=IssSubject}" Margin="3" FontWeight="Bold" FontSize="14"/>
<!--DataTrigger will collapse following panel for simple view-->
<StackPanel Name="IssueDetailPanel" Visibility="Visible" Margin="3">
<StackPanel Width="Auto" Orientation="Horizontal">
<TextBlock Text="Due: " FontWeight="Bold"/>
<TextBlock Text="{Binding Path=IssDueDate}" FontStyle="Italic" HorizontalAlignment="Left"/>
</StackPanel>
<StackPanel Width="Auto" Orientation="Horizontal">
<TextBlock Text="Category: " FontWeight="Bold"/>
<TextBlock Text="{Binding Path=IssCategory}"/>
</StackPanel>
</StackPanel>
</StackPanel>
</Border>
// This is where I have the issue, ShowDetailListItems is in the base class, not the collection
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=ShowDetailListItems, Mode=TwoWay}" Value="False">
<Setter TargetName="IssueDetailPanel" Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
I have learned a TON doing this but this current issue is driving me batty, no luck with google, MSDN, SO or a couple books
I thought I would add this note: I am learning this in order to build some apps for my business, I am a rank beginner in wpf and xaml so I relize this is probably somthing silly. I would really like to find a good tutorial on datacontexts as what I do find is a dozen different "How To's" that are all totally different. I know I have some big holes in my knowledge because I keep ending up with multiple instantiations of my viewmodel class when I try to create references to my datacontext in the codebehind, Window1.xaml and app.xaml files.
Have you tried one of these?
{Binding Path=ShowDetailListItems, ElementName=YourWindowName}
or
{Binding ShowDetailListItems, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}}}

Categories