Getting the ListItem of a Databound list in Silverlight - silverlight

in my Silverlight 4 Application, I have an observable list of a user defined class
ObservableCollection<MyClass> myList;
public class MyClass
{
public string Name { get; set; }
public string Value { get; set; }
}
I display this list in a ListBox, using databinding and a Template for the ListBoxItems:
<ListBox x:Name="ListBoxCharacteristics" Background="{x:Null}" HorizontalAlignment="Left" >
<!-- DataTemplate to display the content -->
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="StackPanelBorder" Orientation="Horizontal" HorizontalAlignment="Left">
<TextBox x:Name="TextBoxCharacteristicName" Style="{StaticResource InputTextBox}" Text="{Binding Name}" />
<TextBox x:Name="TextBoxSep" Style="{StaticResource ReadOnlyTextBox}" Text="=" />
<TextBox x:Name="TextBoxValue" Style="{StaticResource InputTextBox}" Text="{Binding Value}" LostFocus="FormulaTextBox_LostFocus" TextChanged="Formula_TextChanged"/>
<Button x:Name="ButtonCheck" Style="{StaticResource RoundWebdingButton}" Content="s" Click="ButtonCheck_Click" />
<Button x:Name="ButtonAccept" Style="{StaticResource RoundWebdingButton}" Content="a" Click="ButtonAccept_Click" />
<Button x:Name="ButtonRemove" Style="{StaticResource RoundWebdingButton}" Content="r" Click="ButtonRemove_Click" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
The user can change the values in the textboxes and can use the buttons to verify the input, accept it (write it to the underlying model) or remove the entry. To manipulate the underlying model, I need to access the associated item (that is displayed in the listboxitem, where the user has clicked the button).
One idea to get the item was to use the SelectedItem - Property, which will contain the wanted instance of MyClass. Problem is, that clicking on a button or a textbox doesn't select the containing ListBoxItem. The user would have to manually select the Listboxitem first by clicking somewhere at the item, where no textbox or button is displayed. Otherwise, SelectedItem will be null.
I could get the TextBoxCharacteristicName TextBox via the parent object of the Button, but as the user shall be able to change this content too, I would be unable to get the correct item using this property as identifier.
Any other idea, how to find out, which MyClass-instance is the one that is displayed in the corresponding ListBoxItem?
Thanks in advance,
Frank

Found it! The Button has a property "DataContext", that contains the MyClass-object I was looking for!

Related

WPF Clearing Validation in BindingGroup

I have a class called Contact that has 2 string properties: "Name" and "PhoneNumber".
My ViewModel has an observable collection of these contacts and there is a ComboBox that has it's ItemsSource bound to this collection .
I have a WPF Grid whose DataContext is set to the ComboBox's SelectedItem property. This Grid has a BindingGroup that contains a ValidationRule to validate the text properties of my "contact" class.
This is my Grid:
<Grid x:Name="ContainerGrid" Grid.Column="0"
Background="Transparent"
LostFocus="ContainerGrid_LostFocus"
ToolTip="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors)[0].ErrorContent}">
<Grid.BindingGroup>
<BindingGroup x:Name="TextInputBindingGroup" SharesProposedValues="True">
<BindingGroup.ValidationRules>
<local:TextInputValidationRule />
</BindingGroup.ValidationRules>
</BindingGroup>
</Grid.BindingGroup>
<StackPanel>
<TextBlock Text="Name:" />
<TextBox x:Name="PersonName" Text="{Binding Path=PersonName, ValidatesOnExceptions=True}" />
</StackPanel>
<StackPanel>
<TextBlock Text="Phone Number:" />
<TextBox x:Name="PhoneNumber" Text="{Binding Path=PhoneNumber, ValidatesOnExceptions=True}" />
</StackPanel>
</Grid>
If the data for the contact was invalid (the name and/or phone number was missing) the grid containing the elements for editing these properties is highlighted in red and it's tool tip is set to the first error.
If I then delete the invalid Contact, the grid remains outlined in red with the tool tip displaying the error for the item that was deleted even though the grid is now displaying another, valid item.
How do I clear the errors shown in the grid?
Thanks,
-Frinny

In WPF, How to get a Command Parameter from a specific item in a Collection View Source that is bound to a ListView?

My Goal: Right click on a specific item in my ListView, a context menu pops up, select a command, followed by me running a function based on which item's context menu was selected.
My ListView's ItemsSource is bounded to a CollectionViewSource, whose source is an ObservableCollection of "Items".
(ListView, binds -> CollectionViewSource, source -> ObservableCollection of class "Item")
What I tried to do is add a general ContextMenu to all of the "Items" in the listview, and when the context menu item is selected for an item in the ListView, then a command is run. I was able to get the command to run in general, but I haven't been able to get any information/parameters about the specific item whose context menu was chosen.
In this example, "Item" class has a string called host, and I want to pass host string to the RefundRequestCommand but I havent been able to pass any CommandParameters.
I've read some stuff about creating a data template and using that, but haven't been successful. Can anyone guide me / help me out?
Here is some code for reference:
ListView:
<ListView x:Name="ordersList" Margin="0,10,10,0" BorderThickness="2" Grid.Column="2" Grid.Row="0" Grid.RowSpan="2" HorizontalAlignment="Stretch" ItemsSource="{Binding Source={StaticResource cvsOrders}}" SelectionChanged="ordersList_SelectionChanged" SelectedIndex="0" SelectionMode="Extended">
<ListView.Resources>
<local:RefundRequestCommand x:Key="refund"></local:RefundRequestCommand>
</ListView.Resources>
<ListView.ContextMenu>
<ContextMenu>
<MenuItem Header="test" Command="{StaticResource refund}" CommandParameter="{Binding host}"></MenuItem>
</ContextMenu>
</ListView.ContextMenu>
<ListView.View>
<GridView>
<GridViewColumn Width="140">
<GridViewColumnHeader Name="OrderNumber" Click="sortClick" Tag="orderNumber" Content="Order Number" />
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding orderNumber}" TextAlignment="Center"/>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
//On and On.....
Command:
class RefundRequestCommand : ICommand
{
TreeViewFilter treeViewFilter;
public void Execute(object parameter)
{
string host = (string)parameter;
Console.WriteLine(host); //FOR TESTING
}
public bool CanExecute(object parameter)
{
return true;
}
public event EventHandler CanExecuteChanged;
}
Actually you are setting the ContextMenu for ListView but you want to pass ListViewItem over there. You should set the context menu for ListViewItem. Try this.
<local:RefundRequestCommand x:Key="refund"/>
<Style x:Key="MyLVItemStyle" TargetType="ListViewItem">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu>
<MenuItem Header="test"
Command="{StaticResource refund}"
CommandParameter="{Binding host}">
</MenuItem>
</ContextMenu>
</Setter.Value>
</Setter>
</Style>
and use it it you listview like
<ListView x:Name="ordersList" Margin="0,10,10,0" BorderThickness="2" Grid.Column="2" Grid.Row="0" Grid.RowSpan="2" HorizontalAlignment="Stretch"
ItemsSource="{Binding Rectangles}" SelectedIndex="0" SelectionMode="Extended" ItemContainerStyle="{StaticResource MyLVItemStyle}">
......
Also you remove the style applied in ListView and it should work.

How to bind data template of a listbox to class fields

I have a Class Person that consists of FirstName and LastName. I created an object of type ObservableCollection and filled it with some data, bounded it to Listbox.ItemsSource via code-behind. Now I want that data to be displayed on the Window inside a listbox, but via data template, so I can choose which fields of a class to appear..
So, one item would represent FirstName and Lastname in two separate textblocks.
<Window x:Class="PlayList.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:loc="clr-namespace:PlayList"
Title="Media Player PlayList"
Height="300"
Width="300" >
<Grid Height="224" Name="grid1" Width="261" >
<ListBox Height="100" x:Name="listBox1" Margin="12,0,12,124" MouseDoubleClick="listBox1_MouseDoubleClick" >
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Path=FirstName}" />
<TextBlock Text="{Binding Path=Surname}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
Edit:
personae = new ObservableCollection<Person> { per1, per2, per3, per5, per4 }; listBox1.ItemsSource = personae;
Make sure FirstName and LastName are properties, not fields.
Setting the Items Source properly and your example template should be enough
If the ListBoxItem's data context object contains properties string FirstName and string Surname, having the following in the markup would suffice:
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding FirstName}" />
<TextBlock Text="{Binding Surname}"/>
</StackPanel>
</DataTemplate>
You cannot bind to fields, only to properties (which preferably are in a class implementing INPC if you need changes to reflect in the view).

Custom WPF tooltip

I want to create a WPF tooltip containing a label for the header of the tooltip and then a textblock containing more detailed text. I've created the following style in a resource dictionary:
<Style x:Key="AppToolTip"
TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<StackPanel>
<Label Content="{TemplateBinding Content}" FontWeight="Bold" Background="Blue" Foreground="White">
</Label>
<TextBlock Padding="10" TextWrapping="WrapWithOverflow" Width="200">
</TextBlock>
</StackPanel>
</ControlTemplate>
</Setter.Value></Setter>
</Style>
And can successfully apply this style to a button like so and have the tooltip header appear:
<Button.ToolTip>
<ToolTip Style="{DynamicResource PalletToolTip}">
<Binding Source="{x:Static ResStrings.New}"/>
</ToolTip>
</Button.ToolTip>
What i'm stuck on is how can i set the content of the extra descriptive text from the usage above ? I'm already data binding to the Content property when showing the tooltip header.
Anyone who's read Adam Nathan's WPF Unleashed book will recognise that i'm using his example tooltip XAML but in his case, he's used hard coded strings for the content of the label and textblock. I want to create something that's more reusable and hence want to use data binding to achieve the same effect.
I would inherit a HeaderedToolTip class from ToolTip and add a Header property. I would specify the template for that control much as you've done. Then I would be able to use it like so:
<Button>
<Button.ToolTip>
<HeaderedToolTip Header="My Title" Content="My Content"/>
</Button.ToolTip>
</Button>
Or, with bindings:
<Button>
<Button.ToolTip>
<HeaderedToolTip Header="{Binding ToolTipTitle}" Content="{Binding ToolTipText}"/>
</Button.ToolTip>
</Button>
You can use an object or ViewModel that contains all the necessary properties you need in the tooltip.
class MyToolTipViewModel : INotifyPropertyChanged
{
public string Header
{
get{ return mHeader;}
set{ mHeader = value; RaisePropertyChanged("Header"); }
}
public void RaisePropertyChanged(string aProperty)
{
// .. implementation of INotifyPropertyChanged
}
}
then you can set the tolltip directly on an instance of this class.
myButton.ToolTip = new MyToolTipViewModel();
now after that, your tooltip will just show the full qualified name of the ViewModel class.
What you need now is a DataTemplate, which tells WPF how to convert the class into visual object.
<DataTemplate DataType="{x:Type MyToolTipViewModel}">
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
The DataTemplate need to be placed in the resource tree. In the resource section of a higher level object or directly on the application or window resources level.
Hope that helps.

How to modify silverlight combobox data display

I have a combobox defined as follows:
<ComboBox x:Name="cboDept" Grid.Row="0" Margin="8,8,8,8" HorizontalAlignment="Left" VerticalAlignment="Top" Width="120"
ItemsSource="{Binding Source={StaticResource cvsCategories}}">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Width="Auto" Height="Auto">
<sdk:Label Content="{Binding CategoryID}" Height="20" />
<sdk:Label Content="{Binding CategoryName}" Height="20" />
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
It works fine. However, once I select an item in the list, I want a different template to be applied to the combobox selected item being shown to the user (the item shown after the disappearance of popup). In the above case, I want only CategoryName to be displayed in the ComboBox once I select the respective item.
Can anyone let me know on how to achieve this?
thanks
What you need to do is create a ResourceDictionary containing a few defined templates yourself. In the below, ComboBoxTemplateOne and ComboBoxTeplateTwo are user controls that are set out to display the combobox in the manor you want.
<UserControl.Resources>
<ResourceDictionary>
<DataTemplate x:Key="TemplateOne">
<local:ComboBoxTemplateOne />
</DataTemplate>
<DataTemplate x:Key="TemplateTwo">
<local:ComboBoxTemplateTwo />
</DataTemplate>
</ResourceDictionary>
</UserControl.Resources>
You will then need to create your own class that inherits from ContentControl "DataTemplateSelector", overriding OnContentChanged
Protected Overrides Sub OnContentChanged(ByVal oldContent As Object, ByVal newContent As Object)
MyBase.OnContentChanged(oldContent, newContent)
Me.ContentTemplate = SelectTemplate(newContent, Me)
End Sub
You will then need to create another class that inherits from the above DataTemplateSelector which overrides SelectTemplate ("TemplateSelectorClass"), which will return the DataTemplate defined above ("TemplateOne" or "TemplateTwo").
Also in this derived class, you will need to define a property for each of the templates you have
Public Property ComboboxTemplateOne As DataTemplate
Then head back to your XAML and n the blow XAML
<local:TemplateSelectorClass ComboboxTemplateOne="{StaticResource TemplateOne}" Content="{Binding Path=ActiveWorkspace}>
This should work, as it is effectively doing the same work as setting the "DataTemplate" property in WPF (which doesn't exist in SilverLight)
I realise there are a fair few steps here and its quite fiddly, but hopefully this will get you there. Any questions just shout.

Resources