Databound ListPicker SelectedItem goes blank when modifying SelectedIndex - silverlight

I have a ListPicker in my Windows Phone 7 app where both the ItemsSource and SelectedIndex properties are bound to my ViewModel. SelectedIndex is using Two Way binding. The items and the SelectedIndex are correctly populated on application startup. However, when I modify the SelectedIndex property in my ViewModel the ListPicker's TextBox goes blank, as if there was no selected item. If I go to full mode and check which is the selected item from the list, the correct item is being selected.
Here is the ListPicker xaml code:
<toolkit:ListPicker Name="TheListPicker" ItemsSource="{Binding TheItems}" CacheMode="BitmapCache" FullModeHeader="{Binding Path=Resources.TheHeader, Source={StaticResource LocalizedStrings }}" SelectedIndex="{Binding TheCurrentIndex, Mode=TwoWay}" IsEnabled="{Binding IsViewEnabled}" TabIndex="0" >
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="{Binding Name}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="{StaticResource PhoneFontSizeMediumLarge}" />
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<StackPanel x:Name="item" Orientation="Horizontal" Margin="5, 24, 0, 24">
<TextBlock Margin="15, 0, 0, 0" Text="{Binding Name}" FontSize="40" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
This is a simplified version of my ViewModel:
[DataMember]
public ObservableCollection<ItemEntity> TheItems
{
get
{
if (this.theItems == null)
{
this.theItems = new ObservableCollection<ItemEntity>();
}
return this.theItems;
}
set
{
this.theItems = value;
}
}
[DataMember]
public int TheCurrentIndex
{
get
{
return this.theCurrentIndex;
}
set
{
if (value != this.theCurrentIndex)
{
this.theCurrentIndex = value;
NotifyPropertyChanged("TheCurrentIndex");
NotifyPropertyChanged("IsSomeOtherPropertyEnabled");
}
}
}
And here is the relevant code from MainPage.xaml.cs (App_ViewModelChanged is an event handler invoked when some async stuff performed on application startup finishes):
private void App_ViewModelChanged(object sender, ViewModelChangedEventArgs e)
{
BindToViewModel();
}
private void BindToViewModel()
{
this.DataContext = this.ViewModel;
this.ViewModel.IsViewEnabled = true;
}
private void SomeAsyncMethodCompleted(object sender, DetectCompletedEventArgs e)
{
if (e.Error == null)
{
this.ViewModel.TheCurrentIndex = e.Result;
}
}
This issue is not happening all the time. Happens like 50% of the time. It seems to happen only once in the application lifetime and then never happens again. Also, the issue started appearing when I switched from the Feb 2011 release of the Silverlight Control Toolkit to the Aug 2011 release. Never had this issue before.
Is this a known issue?

Ok, so the reason for this strange behavior was a small change in the ListPickerItem style in Aug 2011 Silverlight Control Toolkit.
This is how it looks in Aug 2011 release:
<Style TargetType="controls:ListPickerItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="8 10"/>
<Setter Property="Template">
<!-- More Stuff Here -->
</Setter>
</Style>
And this is how it looked in Feb 2011 release:
<Style TargetType="controls:ListPickerItem">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="Padding" Value="8 6"/>
<Setter Property="Template">
<!-- More Stuff Here -->
</Setter>
</Style>
See the difference? They changed the ListPickerItem Padding to 8 10 in the new release and somehow that was causing the issue. Reverting to 8 6 fixes it. Don't ask me why.
So in my project I didn't actually modified the control toolkit Generic.xaml file, but just modified the specific ListPicker resources to specify a new padding for the ListPickerItem style, like this:
<toolkit:ListPicker Name="TheListPicker" ItemsSource="{Binding TheItems}" CacheMode="BitmapCache" FullModeHeader="{Binding Path=Resources.TheHeader, Source={StaticResource LocalizedStrings }}" SelectedIndex="{Binding TheCurrentIndex, Mode=TwoWay}" IsEnabled="{Binding IsViewEnabled}" TabIndex="0" >
<toolkit:ListPicker.Resources>
<Style TargetType="toolkit:ListPickerItem">
<Setter Property="Padding" Value="8 6"/>
</Style>
</toolkit:ListPicker.Resources>
<toolkit:ListPicker.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center">
<TextBlock Text="{Binding Name}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="{StaticResource PhoneFontSizeMediumLarge}" />
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.ItemTemplate>
<toolkit:ListPicker.FullModeItemTemplate>
<DataTemplate>
<StackPanel x:Name="item" Orientation="Horizontal" Margin="5, 24, 0, 24">
<TextBlock Margin="15, 0, 0, 0" Text="{Binding Name}" FontSize="40" TextWrapping="Wrap" />
</StackPanel>
</DataTemplate>
</toolkit:ListPicker.FullModeItemTemplate>
</toolkit:ListPicker>
Such a small change!

Related

How to properly use radiobutton checked event in mvvm pattern?

When a particular radiobutton is checked/clicked on I want to write some specific text for each of those radiobuttons in a particular textblock. How do I do that following the MVVM pattern ?
In my main viewmodel I have the following code...
public ICommand MyCommand { get; set; }
private string _txt;
public string Txt
{
get
{
return _txt;
}
set
{
_txt = value;
OnPropertyChanged("Txt");
}
}
private bool canexecutemethod(object parameter)
{
if (parameter != null)
{
return true;
}
else
{
return false;
}
}
private void executemethod(object parameter)
{
//what to do here;
}
public MainViewModel()
{
MyCommand = new RelayCommand(executemethod);
}
Also in the xaml:
<RadioButton VerticalAlignment="Center" Margin="15" Content="Name" Command="{Binding MyCommand}"/>
<RadioButton VerticalAlignment="Center" Margin="15" Content="Age" Command="{Binding MyCommand}"/>
<RadioButton VerticalAlignment="Center" Margin="15" Content="DOB" Command="{Binding MyCommand}"/>
<TextBlock Margin="5" Height="30" Width="150" Foreground="Red" Text="{Binding Txt, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
Say when the first radiobutton is checked the textblock text should show some text like
Please enter a name
, when the second radiobutton is checked the textblock text should show some text like
Please enter a valid age/integer value
and so on...
it can be done without view model participation:
<RadioButton VerticalAlignment="Center" Margin="15" Content="Name" Name="chkName"/>
<RadioButton VerticalAlignment="Center" Margin="15" Content="Age" Name="chkAge"/>
<RadioButton VerticalAlignment="Center" Margin="15" Content="DOB" Name="chkDob"/>
<TextBlock Margin="5" Height="30" Width="150" Foreground="Red">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=chkName,Path=IsChecked}" Value="True">
<Setter Property="Text" Value="Please enter a name"/>
</DataTrigger>
<DataTrigger Binding="{Binding ElementName=chkAge,Path=IsChecked}" Value="True">
<Setter Property="Text" Value="Please enter a valid age/integer value"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

Horizontal accordion control?

I want a control whose behavior is as follows:
Act like a Grid
Each child control is embedded in a horizontal Expander (whose header is binded to the control's Tag property)
Each of these Expander has its own ColumnDefinition
Only one of these expanders can be expanded at a time
Non-expanded Expanders' ColumnDefinition have a width set to Auto
The expanded Expander's one is * (Star)
It has to use these exact controls (Grid/Expander), and not some custom ones, so my application's style can automatically apply to them.
I can't seem to find something already made, no built-in solution seems to exist (if only there was a "filling" StackPanel...) and the only solution I can come up with is to make my own Grid implementation, which seems... daunting.
Is there a solution to find or implement such a control?
Here's what I have for now. It doesn't handle the "single-expanded" nor the filling. I don't really know if StackPanel or Expander is to blame for this.
<ItemsControl>
<ItemsControl.Resources>
<DataTemplate x:Key="verticalHeader">
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Expander}}, Path=Header}" />
</DataTemplate>
<Style TargetType="{x:Type Expander}"
BasedOn="{StaticResource {x:Type Expander}}">
<Setter Property="HeaderTemplate"
Value="{StaticResource verticalHeader}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="ExpandDirection"
Value="Right" />
</Style>
</ItemsControl.Resources>
<ItemsControl.Template>
<ControlTemplate>
<!-- Damn you, StackPanel! -->
<StackPanel Orientation="Horizontal" IsItemsHost="True"/>
</ControlTemplate>
</ItemsControl.Template>
<Expander Header="Exp1">
<TextBlock Text="111111111" Background="Red"/>
</Expander>
<Expander Header="Exp2">
<TextBlock Text="222222222" Background="Blue"/>
</Expander>
<Expander Header="Exp3">
<TextBlock Text="333333333" Background="Green"/>
</Expander>
</ItemsControl>
My first thought is to perform this kind of action with a Behavior. This is some functionality that you can add to existing XAML controls that give you some additional customization.
I've only looked at it for something that's not using an ItemsSource as I used a Grid with Columns etc. But in just a plain grid, you can add a behavior that listens for it's childrens Expanded and Collapsed events like this:
public class ExpanderBehavior : Behavior<Grid>
{
private List<Expander> childExpanders = new List<Expander>();
protected override void OnAttached()
{
//since we are accessing it's children, we have to wait until initialise is complete for it's children to be added
AssociatedObject.Initialized += (gridOvject, e) =>
{
foreach (Expander expander in AssociatedObject.Children)
{
//store this so we can quickly contract other expanders (though we could just access Children again)
childExpanders.Add(expander);
//track expanded events
expander.Expanded += (expanderObject, e2) =>
{
//contract all other expanders
foreach (Expander otherExpander in childExpanders)
{
if (expander != otherExpander && otherExpander.IsExpanded)
{
otherExpander.IsExpanded = false;
}
}
//set width to star for the correct column
int index = Grid.GetColum(expanderObject as Expander);
AssociatedObject.ColumnDefinitions[index].Width = new GridLength(1, GridUnitType.Star);
};
//track Collapsed events
expander.Collapsed += (o2, e2) =>
{
//reset all to auto
foreach (ColumnDefinition colDef in AssociatedObject.ColumnDefinitions)
{
colDef.Width = GridLength.Auto;
}
};
}
};
}
}
Use it like this, note you have to add System.Windows.Interactivity as a reference to your project:
<Window ...
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:local="...">
<Window.Resources>
<DataTemplate x:Key="verticalHeader">
<ItemsControl ItemsSource="{Binding RelativeSource={RelativeSource AncestorType={x:Type Expander}}, Path=Header}" />
</DataTemplate>
<Style TargetType="{x:Type Expander}"
BasedOn="{StaticResource {x:Type Expander}}">
<Setter Property="HeaderTemplate"
Value="{StaticResource verticalHeader}" />
<Setter Property="HorizontalContentAlignment" Value="Stretch"/>
<Setter Property="HorizontalAlignment" Value="Stretch"/>
<Setter Property="ExpandDirection"
Value="Right" />
</Style>
<local:ExpanderBehavior x:Key="ExpanderBehavor"/>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<i:Interaction.Behaviors>
<local:ExpanderBehavior/>
</i:Interaction.Behaviors>
<Expander Header="Exp1">
<TextBlock Text="111111111" Background="Red"/>
</Expander>
<Expander Header="Exp2" Grid.Column="1">
<TextBlock Text="222222222" Background="Blue"/>
</Expander>
<Expander Header="Exp3" Grid.Column="2">
<TextBlock Text="333333333" Background="Green"/>
</Expander>
</Grid>
</Window>
The final result:
Edit: Working with ItemsControl - add it to the grid that hosts the items, and add a little to manage the column mapping
public class ItemsSourceExpanderBehavior : Behavior<Grid>
{
private List<Expander> childExpanders = new List<Expander>();
protected override void OnAttached()
{
AssociatedObject.Initialized += (gridOvject, e) =>
{
//since we are accessing it's children, we have to wait until initialise is complete for it's children to be added
for (int i = 0; i < AssociatedObject.Children.Count; i++)
{
Expander expander = AssociatedObject.Children[i] as Expander;
//sort out the grid columns
AssociatedObject.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
Grid.SetColumn(expander, i);
childExpanders.Add(expander);
//track expanded events
expander.Expanded += (expanderObject, e2) =>
{
foreach (Expander otherExpander in childExpanders)
{
if (expander != otherExpander && otherExpander.IsExpanded)
{
otherExpander.IsExpanded = false;
}
}
//set width to auto
int index = AssociatedObject.Children.IndexOf(expanderObject as Expander);
AssociatedObject.ColumnDefinitions[index].Width = new GridLength(1, GridUnitType.Star);
};
//track Collapsed events
expander.Collapsed += (o2, e2) =>
{
foreach (ColumnDefinition colDef in AssociatedObject.ColumnDefinitions)
{
colDef.Width = GridLength.Auto;
}
};
}
};
}
}
Used:
<ItemsControl>
<ItemsControl.Template>
<ControlTemplate>
<Grid IsItemsHost="True">
<i:Interaction.Behaviors>
<local:ItemsSourceExpanderBehavior/>
</i:Interaction.Behaviors>
</Grid>
</ControlTemplate>
</ItemsControl.Template>
<Expander Header="Exp1">
<TextBlock Text="111111111" Background="Red"/>
</Expander>
<Expander Header="Exp2">
<TextBlock Text="222222222" Background="Blue"/>
</Expander>
<Expander Header="Exp3">
<TextBlock Text="333333333" Background="Green"/>
</Expander>
</ItemsControl>
Note, that you'll have to add some logic to manage new/removed children if you have any changes to your ItemsSource!

WPF combobox in a datagrid group header

Is it possible to use a combobox in a datagrid group header to set a selected value for each cells of the appropriate column (this one contains comboboxes)?
Or is there any better solution to set multiple combobox values in a column at the same time?
You can put a ComboBox in the group header like this:
<DataGrid.GroupStyle>
<GroupStyle>
<GroupStyle.ContainerStyle>
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Expander>
<Expander.Header>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Path=Name}" VerticalAlignment="Center" />
<TextBlock Text="{Binding Path=ItemCount}" Margin="5,0,2,0" VerticalAlignment="Center" />
<TextBlock Text="Items" VerticalAlignment="Center" />
<ComboBox Margin="10,0" SelectionChanged="ComboBox_SelectionChanged">
<ComboBoxItem Content="1" />
<ComboBoxItem Content="2" />
<ComboBoxItem Content="3" />
</ComboBox>
</StackPanel>
</Expander.Header>
<ItemsPresenter />
</Expander>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GroupStyle.ContainerStyle>
</GroupStyle>
</DataGrid.GroupStyle>
The SelectionChanged event handler looks something like this:
private void ComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
DependencyObject dependencyObject = sender as DependencyObject;
// Walk up the visual tree from the ComboBox to find the GroupItem.
while (dependencyObject != null && dependencyObject.GetType() != typeof(GroupItem))
{
dependencyObject = VisualTreeHelper.GetParent(dependencyObject);
}
GroupItem groupItem = dependencyObject as GroupItem;
CollectionViewGroup collectionViewGroup = groupItem.Content as CollectionViewGroup;
// Iterate the items of the CollectionViewGroup for the GroupItem.
foreach (object item in collectionViewGroup.Items)
{
// Your logic for changing values here...
}
}

Windows Explorer Tooltip in WPF

its not that hard what i want, but i'm pulling my hairs for days!
i just want the same tooltip behaviour like the WIndows Explorer:
overlay a partially hidden tree/list element with the tooltip that displays the full element
i use the following datatemplate in my treeview
<HierarchicalDataTemplate DataType="{x:Type TreeVM:SurveyorTreeViewItemViewModel}" ItemsSource="{Binding Children, Converter={StaticResource surveyorSortableCollectionViewConverter}}">
<StackPanel x:Name="SurveyorStackPanel" Margin="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Orientation="Horizontal" Height="20" Width="auto">
... (Textblocks, properties, usercontrol, border,... )
<StackPanel.ToolTip>
<ToolTip Placement="RelativePoint" Padding="0" HasDropShadow="False"
DataContext="{Binding ElementName=SurveyorStackPanel}">
<Rectangle HorizontalAlignment="Left" VerticalAlignment="Center"
Width="{Binding ElementName=SurveyorStackPanel, Path=Width}"
Height="{Binding ElementName=SurveyorStackPanel, Path=Height}">
<Rectangle.Fill>
<VisualBrush AutoLayoutContent="True" AlignmentX="Left"
Visual="{Binding}" Stretch="None"/>
</Rectangle.Fill>
</Rectangle>
</ToolTip>
</StackPanel.ToolTip>
</StackPanel>
</HierarchicalDataTemplate>
As you can see, i'm trying to use Visualbrush. but this doesnt work. it only shows what you see on the screen.
I have tried with static resource and binding on a new stackpanel thats in the tooltip, but that only leaves with a blanc tooltip.
Do i something wrong? do i have to use alternatives?
i'm pretty new in WPF. i know the basics, but binding/resources is kinda new for me
EDIT
here is the static source i tried:
<ToolTip x:Key="reflectingTooltip" DataContext="{Binding Path=PlacementTarget, RelativeSource={x:Static RelativeSource.Self}}" Placement="RelativePoint" Padding="0" HasDropShadow="False">
<Rectangle Width="{Binding ActualWidth}" Height="{Binding Path=ActualHeight}" Margin="0"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Rectangle.Fill>
<VisualBrush Visual="{Binding}" Stretch="None" AlignmentX="Left" />
</Rectangle.Fill>
</Rectangle>
</ToolTip>
EDIT 2
Here are a few pics from the situation i have now:
the whole element must be shown when tooltip shows.
before tooltip: http://desmond.imageshack.us/Himg832/scaled.php?server=832&filename=beforedo.png&res=landing
when tooltip is shown: http://desmond.imageshack.us/Himg842/scaled.php?server=842&filename=afterbl.png&res=landing
tooltip has too large height and only shows what screens shows. only problem is to 'fiil in' the hidden text.
VisualBrush renders as a bitmap exactly the same thing you are providing by the 'Visual' property, and it does so without any modification to that thing: it renders them exactly as they are now.
If you want to display something else, you have to provide that something else.. Could you try with something like that: ?
<Window x:Class="UncutTooltip.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">
<StackPanel Orientation="Horizontal">
<ListBox ItemsSource="{Binding}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Width" Value="250" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}"
TextTrimming="CharacterEllipsis">
</TextBlock>
<Grid.ToolTip>
<TextBlock Text="{Binding TheText}"
TextTrimming="CharacterEllipsis">
</TextBlock>
</Grid.ToolTip>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<Border Background="Red" >
<TextBlock Margin="5" Foreground="WhiteSmoke" FontSize="18"
Text="The end of window:)" TextAlignment="Center">
<TextBlock.LayoutTransform>
<RotateTransform Angle="-90" />
</TextBlock.LayoutTransform>
</TextBlock>
</Border>
</StackPanel>
</Window>
---
using System.Collections.Generic;
using System.Windows;
namespace UncutTooltip
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
this.DataContext = new List<Item>
{
new Item { TheText = "its not that hard what i want, but i'm pulling my hairs for days!" },
new Item { TheText = "i just want the same tooltip behaviour like the WIndows Explorer: overlay a partially hidden tree/list element with the tooltip that displays the full element" },
new Item { TheText = "i use the following datatemplate in my treeview" },
new Item { TheText = "As you can see, i'm trying to use Visualbrush. but this doesnt work. it only shows what you see on the screen." },
new Item { TheText = "I have tried with static resource and binding on a new stackpanel thats in the tooltip, but that only leaves with a blanc tooltip." },
new Item { TheText = "Do i something wrong? do i have to use alternatives? i'm pretty new in WPF. i know the basics, but binding/resources is kinda new for me" },
};
}
}
public class Item
{
public string TheText { get; set; }
}
}
Edit:
Now, change the tooltip contents to i.e.:
<Grid.ToolTip>
<ListBox ItemsSource="{Binding TheWholeList}">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<!--<Setter Property="Width" Value="250" />-->
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}" />
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid.ToolTip>
and also change the data definition to:
public class Item
{
public string TheText { get; set; }
public IList<Item> TheWholeList { get; set; }
}
var tmp = new List<Item>
{
.........
};
foreach (var it in tmp)
it.TheWholeList = tmp;
this.DataContext = tmp;
Note that I've commented out the width constraint in the tooltip's listbox, it will present an untruncated list of untruncated elements..
Edit #2:
<StackPanel Orientation="Horizontal">
<ListBox x:Name="listbox" ItemsSource="{DynamicResource blah}"> // <---- HERE
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="Width" Value="250" />
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<Grid Background="Transparent">
<TextBlock Text="{Binding TheText}" TextTrimming="CharacterEllipsis" />
<Grid.ToolTip>
<ToolTip DataContext="{DynamicResource blah}"> // <---- HERE
<TextBlock Text="{Binding [2].TheText}" /> // <---- just example of binding to a one specific item
<!-- <ListBox ItemsSource="{Binding}"> another eaxmple: bind to whole list.. -->
</ToolTip>
</Grid.ToolTip>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
public class Item
{
public string TheText { get; set; }
}
public MainWindow()
{
InitializeComponent();
Resources["blah"] = new List<Item> // <---- HERE
{
new Item { TheText = ........
........
In the last example, I've changed the window.DataContext binding, to a binding to a DynamicResource. In the window init, I've also changed the way the data is passed to the window. I've changed the tooltip template to include the Tooltip explicitely, and bound it to the same resource. This way, the inner tooltip's textblock is able to read the 3rd row of the datasource directly - this proves it is bound to the list, not to the Item.
However, this is crappy approach. It will work only with explicit Tooltip, only with Tooltip.DataContext=resource, and probably, it is the only working shape of such approach.. Probably it'd be possible to hack into the tooltip with attached behaviours and search it's parent window and get the bindings to work, but usually, it's not worth.. Could you try binding to the Item's properties like in the second sample?

wpf listbox checkbox combination, really stuck

I am trying to link the checkbox and listview together, and then use the binding method to set the path to the object in order to set the check-box IsChecked status in the listbox.
List<Test1> datas = new List<Test1>();
var data = new Test1 {Key = 1, Value = "Hello", IsSelected= true};
datas.Add(data);
data = new Test1 {Key = 2, Value = "Hello2", IsSelected= false};
datas.Add(data);
What I need to happen is that if the checkbox is checked ( IsSelected is true ) then I need to populate with those values and then when I click and unclick the checkbox in the GUI I need it to also select the proper listeview item so I can get to the tag property.
This code below does not set the checkbox IsChecked.
<ListBox ItemsSource="{Binding}" Name="lstSwimLane" Width="225" Height="125" SelectionChanged="lstSwimLane_SelectionChanged">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected, Mode=TwoWay}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked" />
<TextBlock Text="{Binding Path=Value}"></TextBlock>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
What do I need to change in order to set the check box to the value in the object? I even tried INotifyChange etc...
Here is the object as well.
public class Test1 : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private bool _isChecked;
public int Key { get; set; }
public string Value { get; set; }
public bool IsSelected
{
get { return _isChecked; }
set
{
if(_isChecked != value)
{
_isChecked = value;
OnPropertyChanged("IsSelected");
}
}
}
protected void OnPropertyChanged(string name)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(name));
}
}
I am new to all this wpf, and I am stumped.
Thanks.
Do you need this to work "three-way"? So setting any of the three properties
ListBoxItem.IsSelected
CheckBox.IsChecked
Item1.IsSelected
will affect both of the other properties? Unfortunately, there is no such Binding in WPF so you're gonna have to do a little extra work.
Update
As pointed out by Robert Rossney, a much better solution for this is to bind
ListBoxItem.IsSelected to Item1.IsSelected
CheckBox.IsChecked to ListBoxItem.IsSelected
Updated Xaml
<ListBox ItemsSource="{Binding}"
Name="lstSwimLane"
SelectionMode="Multiple"
Width="225" Height="125" SelectionChanged="lstSwimLane_SelectionChanged">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected"
Value="{Binding Path=IsSelected,
Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<CheckBox Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked"
IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}},
Path=IsSelected}">
</CheckBox>
<TextBlock Text="{Binding Path=Value}"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
In case anyone is interested, here is the markup for the listbox / combox. It will display horizontal.
Thank you all again for all you help as I greatly appreciated it.
<ListBox ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ItemsSource="{Binding}"
Name="lstSwimLane"
SelectionMode="Multiple"
Width="auto"
Height="auto"
Background="Transparent"
BorderThickness="0"
SelectionChanged="lstSwimLane_SelectionChanged" >
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel IsItemsHost="True" />
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="IsSelected" Value="{Binding Path=IsChecked, Mode=TwoWay}"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" Margin="3,3,3,3">
<CheckBox IsChecked="{Binding RelativeSource={RelativeSource AncestorType={x:Type ListBoxItem}}, Path=IsSelected}"
Checked="CheckBox_Checked"
Unchecked="CheckBox_Unchecked"
VerticalAlignment="Center"
Margin="0,0,4,0" />
<TextBlock Text="{Binding Value}" VerticalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Resources