unable to select listbox item through data template in WPF - 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.

Related

Map control within Listbox

I am trying to display multiple maps within an Listbox.
<Grid Name="MainGrid">
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Visible" >
<WrapPanel Name="wrap" >
<ListBox ItemsSource="{Binding MyList}">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel>
<Border Margin="5" MinWidth="500" MinHeight="400" BorderThickness="2" BorderBrush="Black"
Width="200"
Height="200" >
<esri:MapView MouseDown="MapView_MouseDown" MouseUp="MapView_MouseUp" >
<esri:Map >
<esri:ArcGISTiledMapServiceLayer ID="BaseMap" ServiceUri="http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"/>
</esri:Map>
</esri:MapView>
</Border>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</WrapPanel>
</ScrollViewer>
</Grid>
The map does not get displayed. Only the logo "esri" gets displayed. But if I remove the listbox, it works fine. What could be the issue?
I am very sure that there are items in my listbox otherwise "esri" would not have appeared.
I have tried Itemscontrol as well but its the same result.
In WPF, we don't put UI elements into collections, we put data objects into collections. Each data item should contain whichever properties are needed to data bind to the UI control that you actually want to display. So your MyList collection should contain a collection of data objects.
Once you have created a custom class to contain these required properties, you will then need to declare a DataTemplate that defines which UI element(s) to display. (See the Data Templating Overview page on MSDN for further information on this).
<ControlTemplate x:Key="YourCustomControlTemplate">
<!-- Define your UI content here -->
</ControlTemplate>
Once you have declared your custom ControlTemplate for your custom data class, you should then put it into a Style for your data object:
<Style TargetType="{x:Type YourXamlPrefix:YourCustomDataClass}">
<Setter Property="Template" Value="{StaticResource YourCustomControlTemplate}" />
</Style>
Note that I have omitted the x:Key directive on this Style... you can add one, but without one (and as long as this Style has been declared in scope of the UI), the Style will be automatically applied to your data objects in the collection.

How do I apply a Style Setter for ListBoxItems of a certain DataType?

My WPF ListBox contains two types of object: Product and Brand.
I want my Products selectable. I want my Brands not selectable.
Each of the two types has its own DataTemplate.
By default, anything may be selected:
<ListBox ... >
<ListBox.Resources>
<DataTemplate DataType="{x:Type local:Product}">
...
</DataTemplate>
<DataTemplate DataType="{x:Type local:Brand}">
...
</DataTemplate>
</ListBox.Resources>
</ListBox>
I can set Focusable with a Setter, but then nothing may be selected:
<ListBox ... >
<ListBox.Resources>
...
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Focusable" Value="False" />
</Style>
</ListBox.Resources>
</ListBox>
I cannot put the Setter within the DataTemplate.
I cannot put a DataType onto the Style.
How do I style only the ListBoxItems of type Brand?
Thanks to the StyleSelector class you can attach styles depending on type of data for the ItemContainerStyle. There is a really good example here : http://www.telerik.com/help/wpf/common-data-binding-style-selectors.html
Can you use a data trigger on your ListBoxItem style? If so, bind to the DataContext (your class) and use a value converter to test the type. If it's the one you're interested in, style the ListBoxItem so that it cannot appear selected.
I don't think you can disallow selection of an item in a Selector (parent of ListBox) without codebehind or a custom Behavior.

How to bind to parent position changes in WPF using XAML and no code behind

I have a visio-like interfact but have actual model data behind some of the elements. The elements can be moved by the user.
I use a contentcontrol on a canvas whereby the viewmodels of the elements are places in the content which can then be displayed differently depending on their type but using the same contentcontrol. It is simple to bind the view to the different properties in the viewmodel. However, I have to save the position in the model, and I cannot find a binding solution.
1) The Application.Save Command is handled in the main view model, so I do not have access to the view there. That means I must save the postion data when the elements are moved, or is there a better approach?
2) Assuming that I am right with 1), I am looking to avoid code behind, i.e. I do not want the contentcontrol to deal with the elements that they have in their content. However, so far the code behind version is all I could come up with:
My code behind solution so far:
All model elements implement an interface:
public interface IViewElement
{
String Position { get; set; }
}
And in the contentcontrol:
void ContentControl_LayoutUpdated(object sender, EventArgs e)
{
IViewElement content = this.Content as IViewElement;
content.Position = new Point(Diagram.GetLeft(this), Diagram.GetTop(this)).ToString();
}
The XAML:
<Style TargetType="{x:Type diagram:Item}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type diagram:Item}">
<Grid Canvas.Top="{Binding ElementName=PART_ContentPresenter, Path=Content.Position, Mode=TwoWay}" DataContext="{Binding RelativeSource={RelativeSource TemplatedParent}}"
ContextMenu="{x:Null}">
<!-- PART_ContentPresenter -->
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<DataTemplate DataType="{x:Type local:ViewModel}">
<StackPanel>
...
</StackPanel>
Just encapsulate the codebehind you've used in a Behavior
Why are you using a string to store the position? Use either a Point or two decimal values, and then bind your ContentControl's Canvas.Top and Canvas.Left position to these values using two-way binding.
It will automatically update the model when the Top and Left positions change.
Edit:
Here's an example:
<ContentControl Canvas.Top="{Binding ContentModel.Top, Mode=TwoWay}"
Canvas.Left="{Binding ContentModel.Left, Mode=TwoWay}"
Content="{Binding ContentModel}" />

WPF how to make a listbox/listview unfocusable

I've been trying for a while to display some data in a listbox/listview that would be unfocusable (I mean not only the list, but also the items in it).
I tried with both types of list (listbox and listview), and I used their ItemTemplate and ItemContainerStyle. Everywhere I could, I set the Focusable property to false.
I don't see any other way than disabling the list, but then I have to change all its style, to make it appear not disabled.
Have I missed something? Is there a read-only type of list that I don't know about?
Thank you for your ideas :)
The problem you're probably seeing is that each individual item in the list is focusable. However, you can override that... Try adding this to your listbox:
<ListBox.ItemContainerStyle>
<Style TargetType="Control">
<Setter Property="Focusable" Value="False" />
</Style>
</ListBox.ItemContainerStyle>
Note however that this makes the items unselectable (by keyboard or by mouse). You can set the selected item programmatically, but it doesn't appear to be highlighted automatically any more - so really, this behaves almost the same as an ItemsControl.
Use an ItemsControl with TextBlocks instead of a ListBox
<ItemsControl ItemsSource="{Binding MyListBoxItemsSource}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding MyDisplayName}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>

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