Items in ObservableCollection not updating the view - wpf

I'm stumped here. I have an observable collection that holds business objects. I have it bound to ItemsSource of a ListBox. I am updating the X and Y of my object and it is being displayed correctly in the UI during runtime as it is bound the the Item top and Left. But, here is where the problem is. I have also bound some data to be displayed in textblock text property and the data only displays the initial value. It never updates the textblock Text no matter how many times I change it.
Here is the XAML. If you see a problem with the XAML please let me know. Like I said, the X/Y - Top/Left binding works just fine and updates when changed, the TextBlock that is bound to DisplayData does not.
Also, my business object in my collection does Implement INotifyPropertyChanged.
I will try to make a small demo to replicate this if an answer can not be given just by looking at the XAML.
<Window x:Class="Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="614" Width="674">
<ListBox Name="PlottingBox" Background="White">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayData}" />
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.Template>
<ControlTemplate TargetType="{x:Type ListBox}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Canvas IsItemsHost="True" />
</Border>
</ControlTemplate>
</ListBox.Template>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Canvas.Left" Value="{Binding Path=PlotX}" />
<Setter Property="Canvas.Top" Value="{Binding Path=PlotY}" />
</Style>
</ListBox.ItemContainerStyle>
</ListBox>

I think the problem is in the code behind. Your XAML is absolutely legal and looks good. But before you post the source code make sure the following conditions are true:
Your business object implements INotifyPropertyChanged interface and you raise PropertyChanged event every time DisplayData value is changed.
There are no typos. Neither in DipslayData property definition nor in the PropertyChangedEventArgs, where you pass "DispayData" property name.
DataContext of a ListBoxItem is of your business object's type. Check it with Snoop.
There are no binding errors in runtime. Run your application in debug and check your Output window. You can also check this with Snoop.
Hope after completing this check list you'll have the answer.
Cheers :)

Your business objects need to implement the INotifyPropertyChanged interface, so that the UI is notified of the change and can update to reflect the new value

Uhm, the DataContext inside the ListBox is an item of the ItemsSource list.
for example, if you ListBox is binded to a ObservableCollection, the DataContext inside the ListBox will be a Person object and not the parent's datacontext.
If you have a TextBlock with a binding, the binding will point to a Person Object, in other words, personInstance.DisplayData and not parentDC.DisplayData.
I don't know the behavior without a ItemsSource.
Maybe you know this, but maybe this helps you.

Related

Triggers, commands not firing in custom ListBox's ItemTemplate

I've got a custom ListBox control with a style set up in my Themes/Generic.xaml. I then have a button in the ListBox's ItemTemplate, and it's Click event isn't firing and I've got no idea why. Same goes for the button's Commands (I'm confident the Command issue isn't DataContext related) and interaction triggers. While attempting to debug, I noticed that using the default ListBox instead of my own stopped the problem, but I need to use the custom control.
This is essentially what I've got (fluff removed for brevity). The button:
<controls:CustomListBox>
<ListBox.ItemTemplate>
<DataTemplate>
<Button Click=MyHandler/>
</DataTemplate>
</ListBox.ItemTemplate>
</controls:CustomListBox>
And the custom control's style in Themes/Generic:
<Style TargetType="{x:Type controls:CustomListBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type controls:CustomListBox}">
<Border>
<ScrollViewer Focusable="false" Padding="{TemplateBinding Padding}">
<ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</ScrollViewer>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How can I get this event to fire?
I think this should probably be a usercontrol rather than a custom control.
Are you really going to change the template out of this for something else?
If you use an event handler like that then how are you planning on using the delegate? It's a very inflexible way of working you're headed in.
You mentioned command, which is probably rather more like it.
If you use a button in an item template with a command bound like
<Button Command="{Binding RowCommand}"
Then the datacontext of that Button is the content of the row.
If you bind ItemsSource to a collection Items of ItemVM then it's looking in the ItemVM that is presented to that row.

unable to select listbox item through data template in WPF

I am unable to select desired item from the listbox
(when i click on any item in that listbox the items more than one are getting selected but not the one on which i have clicked). Also the background color of the selected items are getting changed to default (white) color.
The Xaml code used by me is as follows:
<ListBox x:Uid="lst_value" Name="lstValues" Background="Wheat"
BorderBrush="Black" HorizontalAlignment="Left" VerticalAlignment="Top"
BorderThickness="1" Height="100" Width="150" ItemsSource="{Binding listval}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Vertical" Background="Wheat">
<TextBlock x:Name="txtblk" Foreground="Black" FontSize="10"
TextAlignment="Left" FontWeight="Black" Text="{Binding}"
Background="Wheat"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Could anyone let me know how to resolve the issue please.
You should not set the Background on the StackPanel and TextBlock, that obfuscates the selection. To override the background for the selection add resources to your ListBoxItems.
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Style.Resources>
<!-- Selected Brush -->
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Green" />
<!-- Selected but out of focus Brush -->
<SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="LightGreen" />
</Style.Resources>
<!-- If you must set a Background, do it here, should be superfluous though as the ListBox.Background is the same -->
<Setter Property="Background" Value="Wheat" />
</Style>
</ListBox.ItemContainerStyle>
The selection issue can appear if you have a source collection with identical objects (like strings that have the same value).
If I understand your problem right, then the selection logic of the listbox reacts weird. Right?
In most cases, such behaviour has to do with the Equals() or/and the GetHashCode() method of your items (the objects in your listVal enumeration). Make sure you have not multiple objects in your list that return true for a call to Equals() of one object. Make also sure, you have not objects that return changing values for GetHashCode() (some random values).
If you found the problem in the above methods (I assume Equals) but you must say that you can not change the implementation of Equals(), consider creating a wrapper object for your items (a ViewModel).
If i am not wrong listval is a List<string> or any other list of primitive types..
Reason why it is behaving wired with your case is primitive types are struct not classes.
instead of using list of primitive types try using concrete class's
Lets say it you are using List of names like List..
Create a class having name property
class person
{
public string Name{get;set;}
}
use List and you can bind the name property
In my case, I was wrongly adding ComboBoxItems to the ListBox.Items collection. Adding objects other than a "ListBoxItem" to the Items collection caused this behavior for me.

Accessing underlying ViewModel properties within a WPF DataGrid GroupItem DataTemplate

I have a grouped WPF DataGrid (the standard Microsoft one) representing some data on the UI for our users.
In order to show totals within the grouped regions, we are overriding the GroupItem DataTemplate as follows in XAML:
<Style TargetType="{x:Type GroupItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupItem}">
<Border BorderBrush="DarkGray" BorderThickness="1" Padding="12,0">
<Expander VerticalContentAlignment="Center" IsExpanded="{Binding ., Converter={Converters:ExpandedGroupConverter}}" ExpandDirection="Up">
<Expander.Header>
<Canvas>
**<TextBlock Text="{Binding} />**
</Canvas>
</Expander.Header>
<ItemsPresenter/>
</Expander>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
At runtime, currently, the TextBlock text binds to the DataContext, which is a CollectionViewGroup, which makes sense as the grid is binding to a CollectionView wrapping our datasource.
However, the CollectionViewGroup is very limited and does not give us access to its containing ViewModel, where we are storing properties such where to position groups (we're gathering coordinates from the columns when we first layout the grid), and need to bind to them, so that we can, for example, show a total directly above/below given column in a group.
In a nutshell we're trying to access more than just the CollectionView object from within a DataTemplate that targets a GroupItem. Any input on how to do this (or if there is a better approach to get summed totals per columns to show in group total templates) appreciated.
EDIT: So far, a workaround is to have a "Parent ViewModel" property on our items, although this bloats the model, I wish there was a more direct way to do this.
CollectionViewGroup gives you access to all items contained in this group. if you want to access other information from within your template, you can try binding with RelativSource.
EDIT:
so if you have a Collection of ItemVM, and on top of this a CollectionViewGroup on ItemVM.GroupProperty. then you can access your 1st ItemVM within a group with
Binding={ Path = Items[0].AnyPropertyOnItemVM }
i think you will have to use a Converter if you want to calculate or do anything with the GroupItems

WPF problems with trigger syntax

I can't get the following to work. The goal is to change the ZIndex of the usercontrol
when the mouse is over its content.
Using a simple property like "Background" instead of ZIndex does not work either. The compiler complains about "Value 'Grid.IsMouseOver' cannot be assigned to property 'Property'. Object reference not set to an instance of an object." (After compiling and starting the project).
Can someone please provide a working example of a trigger which changes some properties of a different control?
<UserControl x:Class="ImageToolWPF.Controls.sample"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300">
<UserControl.Triggers>
<Trigger SourceName="viewPort" Property="Grid.IsMouseOver" Value="True">
<Setter TargetName="me" Property="UserControl.Panel.ZIndex" Value="2" />
</Trigger>
</UserControl.Triggers>
<Border Name="border" CornerRadius="3,3,3,3" BorderThickness="3" BorderBrush="Green">
<Grid Name="viewPort">
<Label Name="labelTop" HorizontalAlignment="Left" VerticalAlignment="Top" FontSize="16" Background="#a0ffffff" Padding="4"/>
</Grid>
</Border>
</UserControl>
There are a number of issues here:
FrameworkElement.Triggers can contain only EventTriggers, not general triggers. (See the Remarks in the MSDN docs.) You're going to need to move your trigger into a Style instead.
In your Setter, you've specified a TargetName of "me", but there doesn't appear to be any element with that name. I think you mean for the setter to affect the UserControl itself. In that case, if you move the Trigger into a Style on the UserControl, you can just omit the TargetName altogether: setters in a style automatically affect the styled element.
In your Setter, you've specified a Property of UserControl.Panel. That means you are expecting the target (the thing called "me") to have a property called UserControl, and that to have a property called Panel. I think what you are looking for is "(Panel.ZIndex)" - note the brackets showing that this is an attached property name rather than a multipart path, and that there is no UserControl prefix.

Dynamically setting background colour of a Silverlight control (Listbox)

How do I set the background colour of items in a list box dynamically? i.e. there is some property on my business object that I'm binding too, so based on some business rules I want the background colour to be different?
<ListBox Background="Red">
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="Background" Value="Red"/>
</Style>
</ListBox.ItemContainerStyle>
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal"
Margin="5">
<TextBlock VerticalAlignment="Bottom"
FontFamily="Comic Sans MS"
FontSize="12"
Width="70"
Text="{Binding Name}" />
<TextBlock VerticalAlignment="Bottom"
FontFamily="Comic Sans MS"
FontSize="12"
Width="70"
Text="{Binding Age}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
EDIT: It says here
In Silverlight, you must add x:Key
attributes to your custom styles and
reference them as static resources.
Silverlight does not support implicit
styles applied using the TargetType
attribute value.
Does this impact my approach?
Ok - if you need custom logic to determine the background then I would look into building a simple IValueConverter class. You just need to implement the IValueConverter interface and, in its Convert method, change the supplied value into a Brush.
Here's a quick post from Sahil Malik that describes IValueConverters - it might help:
http://blah.winsmarts.com/2007-3-WPF__DataBinding_to_Calculated_Values--The_IValueConverter_interface.aspx
To bind your background to more than one property, you can use IMultiValueConverter. It's just like IValueConverter except that it works with MultiBinding to pass more than one value into a class and get back a single value.
Here's a post I found with a run-through on IMultiValueConverter and MultiBinding:
http://blog.paranoidferret.com/index.php/2008/07/21/wpf-tutorial-using-multibindings/
Edit: If IMultiValueConverter isn't available (it looks like Silverlight only has IValueConverter) then you can always pass your entire bound object (eg your Person object) to an IValueConverter and use various properties from that to return your Brush.
#Matt Thanks for the reply. I'll look into triggers.
My only problem is that, the logic for determining whether a row should be coloured is slightly more involved so I cant just checking a property, so I actually need to run some logic to determine the colour. Any ideas?
I guess I could make a UI object with all the relevant fields I need, but I kinda didnt want to take the approach.
You could try binding something in your controltemplate (ie a border or something) to the TemplateBackground. Then set the background on your listbox to determine the colour it will be.
<Border Margin="-2,-2,-2,0" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1,1,1,0" CornerRadius="11,11,0,0">

Resources