How to display ObserveableCollection<T> in ListBox - wpf

I can't bind to my ObservableCollection<T> within my ListBox.
I am using MVVM, WPF.
The binding for the page works. My understanding is, the Grid (not shown in code) is bound to my DataContext, which is my ViewModel. Therefore, my ListBox can bind to my object called Folders via the Itemssource. My Folders object is very simple, it is literally
private ObservableCollection<Folders> _folders;
public ObservableCollection<Folders> Folders
{
get { return _folders; }
set
{
if (value == _folders)
return;
_folders = value;
OnPropertyChanged("Folders");
}
}
and my Folders model is
public class Folders
{
public string SourceFolder { get; set; }
public string DestinationFolder { get; set; }
}
and lastly, my XAML
<ListBox Grid.RowSpan="2" ItemsSource="{Binding Folders, UpdateSourceTrigger=PropertyChanged}" Grid.Row="1" SelectedItem="{Binding SelectedFolderItem}" IsSynchronizedWithCurrentItem="True">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" DataContext="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}, AncestorLevel=1}, Path=DataContext}">
<StackPanel>
<TextBlock Text="{Binding SourceFolder}" />
<TextBlock Text="{Binding DestinationFolder}" />
<Button Content="Edit" Command="{Binding EditCommand}" CommandParameter="{Binding SelectedListItem}"/>
</StackPanel>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
The button binds/executes, but the 2 textblocks do not. I have also tried
<TextBlock Text="{Binding Folders.SourceFolder}" />
<TextBlock Text="{Binding Folders.DestinationFolder}" />
but it is the same issue. The content is not displayed (not binding) because if I add a watch on my ViewModel, I can see the values are as they should be.
If it helps, if I update my code to
<TextBlock Text="{Binding SelectedFolderItem.SourceFolder}" />
<TextBlock Text="{Binding SelectedFolderItem.DestinationFolder}" />
then it works although this is not desired (it just loops the correct number of times but only for the 1 item!).
Can some one point me in the right direction?

You are setting a different DataContext. You don't need that, otherwise you destroy the purpose of the item template.
<StackPanel Orientation="Horizontal">
<StackPanel>
<TextBlock Text="{Binding SourceFolder}" />
<TextBlock Text="{Binding DestinationFolder}" />
<Button Content="Edit"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListBox}}, Path=DataContext.EditCommand}"
CommandParameter="{Binding}"/>
</StackPanel>
</StackPanel>
of course if you want the buttons to work aswell, you just move the relative source you currently have to the binding of the button.

Related

Binding to a variable in view-model

This is my XAML code:
<ItemsControl MinHeight="400" BorderThickness="0" ItemsSource="{Binding People}" ItemTemplateSelector="{StaticResource myItemsTemplateSelector}" />
And in the resources:
<UserControl.Resources>
<DataTemplate x:Key="ItemTemplate">
<Button CommandParameter="{Binding}" Command="{Binding RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}, Path=DataContext.SelectSomething}" >
<StackPanel>
<TextBlock Text="{Binding Cars[0].Name}"></TextBlock>
<TextBlock Text="{Binding Cars[0].Description}"></TextBlock>
</StackPanel>
</Button>
</DataTemplate>
<selectors:MySelector ItemTemplate="{StaticResource ItemTemplate}" x:Key="myItemsTemplateSelector" />
</UserControl.Resources>
People is an ObservableCollection in my view-model. Person is a class which has this property:
public IList<Cars> Cars { get; set; }
As you can see, I bind the textblocks to Cars[0].Name and Cars[0].Description. The problem I have is the 0. Instead of 0 it needs to be a variable which is defined in my view-model. How can I make the binding to be something like this:
<TextBlock Text="{Binding Cars[variableInVM].Name}"></TextBlock>
I fixed this issue by creating a new class which has these properties: Person, Cars and CurrentCars. Then, instead of binding with Cars[index], I bind with CurrentCars.
You could use an IMultiBindingConverter, and then use a MultiBinding that binds Cars and variableInVM and uses your converter.
The converter simply returns the variableInVM's element of Cars.
Inside that element, you would have the DataTemplate shown in your question, and it would use the Car passed in.

MVVM : Binding Commands with Collection to Listbox in WPF

i have a list box in which there are different controls like button , text box etc in its item template, i have a collection which i bind with listbox, it works fine , but now i want to move my code to MVVM , and i write some commands in my View Model for clicks events of buttons , how can i bind my collection + my commands to list box ??? because commands are not in the collection, this is the Data Template for my list Box
<DataTemplate x:Key="listItemTemplate">
<Grid ShowGridLines="False">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<DockPanel Grid.Row="0" Name="commentsPanel" LastChildFill="False" MinWidth="350">
<TextBlock Name="txtUserName" IsEnabled="False" Text="{Binding UserName}"
Width="Auto" DockPanel.Dock="Left" Foreground="GhostWhite" Margin="0,6,0,0"></TextBlock>
<TextBlock Name="txtDate" IsEnabled="False" Text="{Binding CreateDt}"
Width="Auto" DockPanel.Dock="Left" Foreground="Green" Margin="4,6,0,0"></TextBlock>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" Width="{Binding EditPanelWidth}" x:Name="EditDeletePanel" Visibility="{Binding ButtonVisibilityText }">
<Button Name="btnEdit" Content="Edit" Width="Auto" DockPanel.Dock="Right" Height="20"
Click="btnEdit_Click_1" Margin="4,4,0,4" Foreground="GhostWhite" VerticalContentAlignment="Top" Visibility="{Binding ButtonVisibilityText}"></Button>
<Button Name="btnDelete" Content="Delete" Width="Auto" Height="20" VerticalContentAlignment="Top" DockPanel.Dock="Right" Visibility="{Binding ButtonVisibilityText}"
Click="btnDelete_Click_1" Margin="4"></Button>
</StackPanel>
<StackPanel DockPanel.Dock="Right" Orientation="Horizontal" x:Name="SaveCancelPanel" Visibility="{Binding CancelSaveEnableText}">
<Button Name="btnSave" Content="Save" Width="Auto" Height="20" DockPanel.Dock="Right"
Click="btnSave_Click_1" Margin="4"></Button>
<Button Name="btnCancel" Content="Cancel" Height="20" Width="Auto" DockPanel.Dock="Right"
Click="btnCancel_Click_1" Margin="4"></Button>
</StackPanel>
</DockPanel>
<dxe:TextEdit ShowBorder="False" Grid.Row="1" Name="txtComment" Width="Auto" Foreground="Red"
TextWrapping="WrapWithOverflow" EditValue="{Binding Note}" IsEnabled="{Binding IsCommentTextEnable}">
</dxe:TextEdit>
<dxe:TextEdit Text=".............." Grid.Row="2" ShowBorder="False" IsEnabled="False">
</dxe:TextEdit>
</Grid>
</DataTemplate>
and here is the collection + my commands which i want to bind to buttons ,
public ICommand CancelCommand
{
get { return _cancelCommand ?? (_cancelCommand = new CommandHandler(Cancel)); }
set { _cancelCommand = value; }
}
public TList<ProgramNote> NotesCollection
{
get { return _notes; }
set
{
_notes = value;
RaisePropertyChanged("NotesCollection");
}
}
I know i can use this code to bind my commands with button
<Button Command={Binding CancelCommand}
but this command is not present in the collection , i am new in MVVM , kindly help , may be i am missing some little thing to bind my commands , but i am confused that how to add commands in my collection , so that i can get them in my view
You can bind the commands to your data template buttons etc by finding the appropriate viewmodel
example
<DataTemplate x:Key="listItemTemplate">
<Button Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=LixtBox}}"
CommandParameter="{Binding}">
</DataTemplate>
in example we'll find the datacontext of LixtBox which I assume to be your viewmodel then will bind to the command and pass the current object as the command parameter to perform actions on.
you'll then receive the item as parameter in the implementation of your command
public void Execute(object parameter)
{
ProgramNote note = parameter as ProgramNote;
//your logic here, eg cancelling download etc.
}
Thanx to all of you specially thanx to #Sheridan and #PushPraj, I am able to do it now , here is the code of data template in which i have a button
<Button Name="btnCancel" Content="Cancel" Height="20" Width="Auto" DockPanel.Dock="Right"
Command="{Binding DataContext.CancelCommand, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=dxe:ListBoxEdit}}"
CommandParameter="{Binding}" Margin="4"></Button>
and this is the code of ListBox
<dxe:ListBoxEdit Name="listComments" Grid.Row="1" ItemTemplate="{StaticResource listItemTemplate}"
ItemsSource="{Binding NotesCollection}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.VerticalScrollBarVisibility="Visible" >
</dxe:ListBoxEdit>
and lastly this is my back end code
listComments.DataContext = viewModel;
It's always difficult to answer new user's questions because they always leave out important information from their questions. However, judging by your question text, it sounds to me like you have set your collection property as the DataContext of your Window. If you want to data bind to your commands instead, then you'll need to change the DataContext to the object that contains both the collection and the commands... an instance of your view model:
DataContext = new YouViewModel();
Now that the DataContext is set to an instance of your view model, you can data bind to its properties as you showed us:
<Button Command="{Binding CancelCommand}" />
...
<ListBox ItemsSource="{Binding NotesCollection}" />
Ahhh, sorry I misunderstood - so your Button is inside the ListBox. In that case, you could try something like this:
<Button Command="{Binding DataContext.CancelCommand,
RelativeSource={RelativeSource AncestorType={x:Type Window}}}" />
This should reach out of the scope of the collection, looking in the DataContext of a Window, so if you have set the instance of your view model to the Window.DataContext, this should work.

silverlight Grid ListBox row change color

I've got a silverlight phone app that i'm using to learn. It's using web services to get the data. I've got a Grid with a listbox inside of the grid. I'd like to be able to have some of the text inside that listbox be a certain color based on conditions.
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding Operation}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding Status}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
<TextBlock Text="{Binding DateTimeStart1}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
<TextBlock Text="{Binding DataID}" Visibility="Collapsed"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
So if the textblock which is bound to Status.. if that status = true i want to change the color of the row or even just that textbox.... whatever is easier.
i'm binding the data using the following code
Me.MainListBox.ItemsSource = e.Result
Any help would be great
~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is the modified code
Private Class SomeData
Inherits wsData.Data_Queries
Public Property RowColor As SolidColorBrush
End Class
And here is where i'm creating a new instance of the class and binding it to the listbox
Dim oSomeDataS As New List(Of SomeData)
For Each x In e.Result
Dim oSomeData As New SomeData
With x
oSomeData.DataID = .DataID
oSomeData.DateTimeStart1 = .DateTimeStart1
oSomeData.FinishFromStart = .FinishFromStart
oSomeData.Operation = .Operation
oSomeData.ShortDate = .ShortDate
oSomeData.Status = .Status
oSomeData.TblComputerNameID = .TblComputerNameID
oSomeData.TblOperationID = .TblOperationID
oSomeData.TblStatusID = .TblStatusID
oSomeData.TblSiteID = .TblSiteID
If .Status = "False" Then
oSomeData.RowColor = New SolidColorBrush(Colors.Red)
Else
oSomeData.RowColor = New SolidColorBrush(Colors.Green)
End If
oSomeDataS.Add(oSomeData)
End With
Next
Me.MainListBox.ItemsSource = oSomeDataS.OrderBy(Function(o) o.Operation)
And the XAML code
<ListBox x:Name="MainListBox" Margin="0,0,-12,0" SelectionChanged="MainListBox_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Margin="0,0,0,17" Width="432">
<TextBlock Text="{Binding Operation}" TextWrapping="Wrap" Style="{StaticResource PhoneTextExtraLargeStyle}"/>
<TextBlock Text="{Binding Status}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}" Foreground="{Binding RowColor}"/>
<TextBlock Text="{Binding DateTimeStart1}" TextWrapping="Wrap" Margin="12,-6,12,0" Style="{StaticResource PhoneTextSubtleStyle}"/>
<TextBlock Text="{Binding DataID}" Visibility="Collapsed"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
There are lots of different ways to do this. The easiest to understand is to use an IValueConverter to bind the background color of the row to a color based on the status. A good example of this can be found here (the example is WPF, but the same principle applies in Silverlight).
Another way to do this is to wrap your data objects (that you get from e.Result) in a ViewModel object. The ViewModel object would expose all the same properties as your data, but would also add one property (perhaps RowColor) where the getter returned a Brush or Color based on the value of Status. You'd have to loop through your e.Results and create a new ViewModel for each one, add the ViewModels to a list, then set the ItemsSource to that new list of ViewModels.

silverlight binding grid column from a comboBox using xaml element Binding

I want to add a unbound column to a a xamgrid but I want to get the value from a combox, the combobox is bound to channel object at runtime.
I tried adding the unbound column.
Tried:
<Binding Source="{StaticResource ChannelTemplate}" Path="SelectedItem.Content" />
But it doesn't seem to work
I want to bind the channelcode to the grid.
Grid,class code below.
<UI:BaseControl.Resources>
<DataTemplate x:Key="ChannelTemplate" >
<StackPanel Orientation="Horizontal" >
<TextBlock Margin="2" Text="{Binding Path=ChannelName}"/>
</StackPanel>
</DataTemplate>
</UI:BaseControl.Resources>
<ComboBox Name="CboChannel" Margin="59,6,0,0" Height="22" Width="200" VerticalAlignment="Top" HorizontalAlignment="Left" ItemTemplate="{StaticResource ChannelTemplate}" >
</ComboBox>
<ig:XamGrid Name="grdInventory" ColumnWidth="auto" ....
<ig:XamGrid.Columns>
<ig:UnboundColumn Key="Channel">
<ig:UnboundColumn.ItemTemplate>
<DataTemplate >
<TextBlock >
<TextBlock.Text >
<Binding Source="{StaticResource ChannelTemplate}" Path="SelectedItem.Content" />
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ig:UnboundColumn.ItemTemplate>
</ig:UnboundColumn>
</ig:XamGrid.Columns>
</ig:XamGrid>
Code:
Public Class Channel
Implements IDisposable
<DataMember(IsRequired:=True)>
Public Property ChannelID As Long
<DataMember()>
Public Property ChannelDescription As String
<DataMember()>
Public Property ChannelName As String
<DataMember()>
Public Property ChannelCode As String
End Class
Thanks
J
You could try using a DataContext pattern so that your unbound column and combobox selected item are both bound to the same value. For example (I'm using the standard data grid here):
1) Create a DataContextProxy class based on this article: http://weblogs.asp.net/dwahlin/archive/2009/08/20/creating-a-silverlight-datacontext-proxy-to-simplify-data-binding-in-nested-controls.aspx
2) Create a static resource to the DataContextProxy on the page that contains the grid and combo e.g.
<UserControl.Resources>
<Silverlight:DataContextProxy x:Key="Proxy"></Silverlight:DataContextProxy>
</UserControl.Resources>
3) Bind the SelectedValue property of your combo to an exposed property on your VM e.g.
<ComboBox Height="23"
HorizontalAlignment="Left"
SelectedValue="{Binding TestProperty, Mode=TwoWay}"
SelectedValuePath="Content"
Margin="126,54,0,0" x:Name="comboBox1"
VerticalAlignment="Top" Width="120" DisplayMemberPath="Content">
<ComboBox.Items>
<ComboBoxItem x:Name="x" Content="test2"></ComboBoxItem>
<ComboBoxItem x:Name="x2" Content="test"></ComboBoxItem>
</ComboBox.Items>
</ComboBox>
4) Bind the grid column template to the same VM property e.g.
<sdk:DataGrid AutoGenerateColumns="True"
Height="120"
HorizontalAlignment="Left"
Margin="155,153,0,0"
Name="dataGrid1"
VerticalAlignment="Top" Width="120">
<sdk:DataGrid.Columns>
<sdk:DataGridTemplateColumn Header="Test">
<sdk:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Mode=OneWay,Source={StaticResource Proxy},Path=DataSource.TestProperty}">
</TextBlock>
</DataTemplate>
</sdk:DataGridTemplateColumn.CellTemplate>
</sdk:DataGridTemplateColumn>
</sdk:DataGrid.Columns>
</sdk:DataGrid>
This worked for me when I knocked up a quick test app. The DataGridTemplateColumn stays in sync with the selected combobox item.

Any way to reuse Bindings in WPF?

I'm getting to the point in a WPF application where all of the bindings on my controls are getting quite repetitive and also a little too verbose. Also if I want to change this binding I would have to change it in various places instead of just one.
Is there any way to write the source part of the binding once such as in a resource and then reuse it by referencing it with a more compact syntax. I've looked around for such capabilities but I haven't found it.
What I'm doing now
<StackPanel>
<ToggleButton x:Name="someToggleButton" />
<Button Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
<Grid Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
<TextBox Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
<CheckBox Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel>
What I want to be able to do (Pseudocode)
<StackPanel>
<StackPanel.Resources>
<Variable x:Name="someToggleButtonIsChecked"
Type="{x:Type Visibility}"
Value="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}" />
</StackPanel.Resources>
<ToggleButton x:Name="someToggleButton" />
<Button Visibility="{VariableBinding someToggleButtonIsChecked}" />
<Grid Visibility="{VariableBinding someToggleButtonIsChecked}" />
<TextBox Visibility="{VariableBinding someToggleButtonIsChecked}" />
<CheckBox Visibility="{VariableBinding someToggleButtonIsChecked}" />
</StackPanel>
Is there any similar type of similar feature or technique that will allow me to declare the binding source once and then reuse it?
You can just bind someToggleButton's IsChecked property to a property on your viewmodel (the DataContext) and use that. It would look something like this:
<StackPanel>
<ToggleButton x:Name="someToggleButton" IsChecked="{Binding ToggleVisibility, Mode=TwoWay, Converter={StaticResource BoolToVisibilityConverter}}" />
<Button Visibility="{Binding ToggleVisibility}" />
<Grid Visibility="{Binding ToggleVisibility}" />
<TextBox Visibility="{Binding ToggleVisibility}" />
<CheckBox Visibility="{Binding ToggleVisibility}" />
</StackPanel>
This would require that your Window's DataContext has a property called ToggleVisibility of type Visibility.
EDIT:
To eleborate further, your viewmodel could look like this:
public class SomeViewModel : INotifyPropertyChanged
{
private Visibility toggleVisibility;
public SomeViewModel()
{
this.toggleVisibility = Visibility.Visible;
}
public Visibility ToggleVisibility
{
get
{
return this.toggleVisibility;
}
set
{
this.toggleVisibility = value;
RaisePropertyChanged("ToggleVisibility");
}
}
private void RaisePropertyChanged(string propertyName)
{
if (this.PropertyChanged != null)
this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
#region INotifyPropertyChanged Members
public event PropertyChangedEventHandler PropertyChanged;
#endregion
}
And you would then set an instance of it as the DataContext on the Window or even just on the StackPanel
Is there any way to write the source part of the binding once such as in a resource and then reuse it by referencing it with a more compact syntax.
Perhaps you can do that with PyBinding. I don't know the extent of its capabilities, but I use it all the time ot avoid type converters. Here is an example I use a lot.
Visibility="{p:PyBinding BooleanToVisibility(IsNotNull($[.InstanceName]))}"
BooleanToVisibility is a function I wrote in IronPython.
$[.InstanceName] binds to the InstanceName property of the current data-bound item.
EDIT: You can also use this to bind one UI's property to another's. Here is some info from the help file.
$[NameTextBlock.Text] - The text property of the element with x:Name equal to "NameTextBlock"
$[NameTextBlock] - An actual TextBlock instance, rather than one of its properties
$[{Self}] - Bind to your self. Equivalent to {Binding RelativeSource={RelativeSource Self}}
$[{Self}.Text] - The Text property off your self. Equivalent to {Binding Path=Text, RelativeSource={RelativeSource Self}}
http://pybinding.codeplex.com/
Untested Theory
<StackPanel>
<ToggleButton x:Name="someToggleButton" />
<Button Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}" />
<Grid Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}"/>
<TextBox Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}"/>
<CheckBox Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}"/>
</StackPanel>
Second Attempt
<StackPanel>
<ToggleButton x:Name="someToggleButton" />
<Button Name="myButton" Visibility="{p:PyBinding BooleanToVisibility($[someToggleButton.IsChecked])}" />
<Grid Visibility="{p:PyBinding $[myButton.Visibility]}"/>
<TextBox Visibility="{p:PyBinding $[myButton.Visibility]}"/>
<CheckBox Visibility="{p:PyBinding $[myButton.Visibility]}"/>
</StackPanel>
Just looking at the original code, you could group the necessary elements into their own container and then manage the container Visibility:
<StackPanel>
<ToggleButton x:Name="someToggleButton" />
<StackPanel Visibility="{Binding ElementName=someToggleButton, Path=IsChecked, Converter={StaticResource BoolToVisibilityConverter}}">
<Button />
<Grid />
<TextBox />
<CheckBox />
</StackPanel>
</StackPanel>
Actually, today I would do this with the VSM - have a state with the elements Visible and a state with them not Visible, then use two GoToState Behaviors on the Toggle button to set the state based on the button's toggle state.

Resources