ListView selecting single item , wpf - wpf

i have a listview and it's items source is a list . I want a user to pick only one item . When I set SelectionMode of the listview to single , the user can still select several items and it seems like the listview is going crazy and selects items that user didn't select... looks very strange... can anyone know what could be the problem?
I cann't paste here a screenshot , i don't have the paste option.....
this is a xaml -
<StackPanel MinWidth="600" Margin="0,0,0,10" HorizontalAlignment="Left" Width="600">
<GroupBox Header="Command Queue" BorderThickness="0" Foreground="CornflowerBlue">
<Border BorderThickness="1.5" CornerRadius="10">
<ListView SelectionMode="Single" Background="Transparent" BorderThickness="0" Margin="5" Name="ListView_CmdQ" ItemsSource="{Binding}" MaxHeight="450" FontFamily="verdana" FontSize="12">
</ListView>
</Border>
</GroupBox>
</StackPanel>

Do the items in your list appear more than once? I've seen this problem before where you have something like this:
var a = new Thing();
var b = new Thing();
var myList = new List<Thing>();
myList.Add(a);
myList.Add(b);
myList.Add(a);
myList.Add(b);
If you were to bind a ListView to the myList, you'd get the behaviour you've described. I think basically it's to do with the fact that multiple items in the list match the SelectedItem, so the styling of the list gets a bit confused. One way around it is to wrap each item in another class:
var myList = new List<WrappedThing>();
myList.Add(new WrappedThing((a));
myList.Add(new WrappedThing((b));
myList.Add(new WrappedThing((a));
myList.Add(new WrappedThing((b));
... which means that each item in the list is unique, even though the item they're wrapping may not be.

If your list_listItems contains the same string twice, you get this behavior. This happens with value types and reference strings. You should probably wrap each string in a TextBlock and put that in the listview.
It looks like this is reported as a bug still active (since 2007) here.

Related

WPF [VB] Bring selected ListViewItem to front and overlap other ListViewItems

I am making an explorer control in my WPF application using a Treeview and Listview. For the Listview I would like to show the contents of the selected folder using 32x32 pixel icons. To achieve this I have the following XAML which is also truncating long file/folder names within a TextBlock:
<ListView x:Name="LV_Explore"
Grid.Column="2"
BorderThickness="0"
VerticalContentAlignment="Top"
ItemsSource="{Binding Path=., Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Visible"
AllowDrop="True">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel Orientation="Horizontal" />
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<StackPanel x:Name="SP_ExploreItem"
Width="42">
<Image Source="{Binding LargeIcon, UpdateSourceTrigger=PropertyChanged}"
Margin="0,0,0,2" />
<TextBlock x:Name="TXT_ExploreItem"
Width="42"
Height="42"
TextOptions.TextFormattingMode="Display"
TextAlignment="Center"
TextWrapping="Wrap"
TextTrimming="WordEllipsis"
LineStackingStrategy="BlockLineHeight"
Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" />
</StackPanel>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
When selecting a ListViewItem I would like for it to overlap the items (files/folders) below rather than the current behaviour which is to increase the height of all of the ListViewItems in the current row of the WrapPanel.
To get to the ListViewItem I am using the following code in order to show all of the text in the TextBlock when an item is selected:
Private Sub LV_Explore_SelectionChanged(sender As Object, e As SelectionChangedEventArgs) Handles LV_Explore.SelectionChanged
If LV_Explore.SelectedItems.Count = 1 Then
Dim LVItem As ListViewItem = TryCast(Me.LV_Explore.ItemContainerGenerator.ContainerFromIndex(Me.LV_Explore.SelectedIndex), ListViewItem)
If LVItem IsNot Nothing Then
Dim BD As Border = TryCast(VisualTreeHelper.GetChild(LVItem, 0), Border)
If BD IsNot Nothing Then
Dim CP As ContentPresenter = TryCast(BD.Child, ContentPresenter)
If CP IsNot Nothing Then
Dim SP As StackPanel = TryCast(LVItem.ContentTemplate.FindName("SP_ExploreItem", CP), StackPanel)
If SP IsNot Nothing Then
Dim TB As TextBlock = TryCast(SP.FindName("TXT_ExploreItem"), TextBlock)
TB.TextTrimming = TextTrimming.None
TB.Height = Double.NaN
End If
End If
End If
End If
End If
End Sub
For reference I want to achieve similar to the behaviour of the desktop e.g: Screenshot 1: Normal state Screenshot 2: Selected state
Whereas I currently have the normal state working fine and the selected state appears like this: Screenshot 3: Listview selected state
I suspect that I may need to change from using a WrapPanel to a Canvas in the ItemsPanelTemplate of the ListView control which will then lose the behaviour of lining up and wrapping items within the Listview automatically. So I am looking for suggestions of the best approach to use here to maintain the layout and to allow me to overlap items when selected.
Thanks,
Jay
As you may realise, something in a listview item is inside a sort of a box and it isn't getting out of that. It will be truncated or clipped if you don't let it grow.
A canvas doesn't clip so that would tick one box of your requirement, you would have to write a custom panel based on a canvas or the base panel control and write your own measure/arrange code.
This is non trivial and any scrolling could make it even more complicated, but you could take a look at people's work:
eg
https://www.codeproject.com/Articles/37348/Creating-Custom-Panels-In-WPF
Alternately, you could use an adorner.
The adorner layer is on top of everything else in your window but still in the same datacontext of whatever it's associated with.
These are not so easy to work with but again you could base your code on someone else's:
http://www.nbdtech.com/Blog/archive/2010/07/12/wpf-adorners-part-4-ndash-simple-and-powerful-system-for.aspx
An adorner would probably be my first candidate, if I follow what you want correctly.
Not many people use vb nowadays and I'm afraid you're going to find pretty much every time you look at samples they are c#.

How to format text in a ListBox where items are added dynamically?

I need to format the text in each textblock, which is an item of my ListBox. I have managed to make something but it doesnt look the same as if I would call the Console.WriteLine-Method.
private void addNewGameItem(string gamename, string lastChangedDate)
{
ListBoxItem lbi = new ListBoxItem();
StackPanel stpl = new StackPanel();
TextBlock tbl = new TextBlock();
tbl.Text = String.Format("{0,-100} {1,30}", gamename, lastChangedDate);
tbl.FontSize = textBlockFontsize;
stpl.Orientation = Orientation.Horizontal;
stpl.Children.Add(tbl);
lbi.Content = stpl;
savedGamesList.Items.Add(lbi);//Add the ListBoxItem to the ListBox
}
The problem is that if the gamename is longer than for example the previous one, the date will appear futher right. How can I format this, that it doesnt matter how long the gamename is, so the date-string will start on the same position, in each textblock?
This might be easier done in XAML rather than the code behind as you can more easily define the DataTemplate to be used to display your list items that way. Make your list of games an ObservableCollection and bind the ItemsSource of your list box to that. This will mean it auto-updates when you add a new item to the list.
Then you can split the string into two parts, one for the game name the other for the date:
<ListBoxItem ...>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding GameName}"/>
<TextBlock Text="{Binding LastChangedDate}"
HorizontalAlignment="Right"/>
</StackPanel>
</DataTemplate>
</ListBoxItem>
Then in the ListBox style definition include the line:
HorizontalContentAlignment="Stretch"
This will make each list box item fill the entire width of the list box and (as long as the dates are formatted so that they all come out the same length) align them correctly.

DataBinding to a List in silverlight

I've got a Grid which contains a TextBlock. The Grid's DataContext is of type List<MyClass>, and I'd like to bind the TextBlock.Text property to the MyClass.MyProperty property of first element in the List. I tried something like:
<Grid x:Name="RootLayout">
<TextBlock Text="{Binding [0].MyProperty}" />
</Grid>
But of course, that did not work. What's the right way of doing this?
Edit:
I'm going to try and make my explanation more clear. I've got multiple elements in the grid, each of which binds to a different item in the list. The items are laid out in a customized manner which cannot be accomplished by a GridView or ListBox. One of the items in the Grid is the TextBlock, and I'd like to bind its Text property to a property of the first element in the list. Once I know how to do that, I can extend that knowledge to add bindings to the rest of the elements in the grid.
Edit 2:
Turns out, my code works just fine in Silverlight. My project is actually a WinRT project, but I figured I'd get quicker answers if I tagged it as Silverlight, since databinding is supposed to work the same. I'm assuming this is a bug in WinRT, so I'll just have to find a workaround for it :(
I'm not sure I get why you want to do this, but you could create a property that returns what you want from the item in the list like so:
public string MyBindingProperty
{
get { return MyList != null && MyList.Count > 0 ? MyList[0].MyProperty : "Error Text"; }
}
Then you'd bind to MyBindingProperty:
<TextBlock Text="{Binding MyBindingProperty}" />
EDIT
I was wrong in saying you can't get at the items in the List - my bad. Your binding should look like this:
<TextBlock Text="{Binding [0].MyProperty}" />
If you need me I'll be in the corner enjoying my humble pie.
I am not an expert of SL but I think your are using the wrong Grid object; try with DataGrid in this way:
<data:DataGrid x:Name="targetDataGrid">
<data:DataGrid.Columns>
<data:DataGridTextColumn Header="MyProperty"
Binding="{Binding MyProperty}" />
</data:DataGrid.Columns>
</data:DataGrid>
also see here for more details: Defining Silverlight DataGrid Columns at Runtime
Edit: then go this way:
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Text="{Binding MyProperty}" />
</Grid>
found here: http://msdn.microsoft.com/en-us/library/cc278072%28v=VS.95%29.aspx scroll donw the article...

How to check against Content name of the ListBoxItem?

I was able to toggle visibility of menuItem based on the property of another content (data in the datagrid). I need to be able to achieve the same with listbox items. I have hard time doing so. I am wondering if someone can help with that: Any help is highly appreciated!
The way it worked with menuItems:
XAML:
<SplitButton:MenuButton x:Name="test" Content="Test">
<SplitButton:MenuButton.ButtonMenuItemsSource>
<toolkit:MenuItem x:Name="item1" Header="Item1" />
<toolkit:MenuItem x:Name="item1" Header="Item2" />
</SplitButton:MenuButton.ButtonMenuItemsSource>
I have problem with setting the same var for lisboxitem listbox. It is working perfect with MenuItems.
var item1Task = test.ButtonMenuItemsSource.OfType<ListBoxItem>().Where(temp => temp.Name == "item1").First();
I need to convert this coede line of setting var in a way that it will work with ListBoxItem:
Listbox XAML:
<toolkit:Expander x:Name="test" Header="Test">
<Border x:Name="Border">
<ListBox x:Name="List">
<ListBoxItem x:Name="item1" Content="Item1" />
<ListBoxItem x:Name="item2" Content="Item2" />
</ListBox>
I cannot use OfType within listbox content. I tried something like that:
var item1Task = List.OfType<ListBoxItem>().Where(temp => temp.Content == "item1").First();
I hope it is possible. It is working great with the previous control, but I need to do the same using listbox. Thank you in advance for the help.
Do you need to find ListBoxItem named "item1" or which has "item1" as Content? To do the first, you may try this query:
var item1Task = List.Items.Cast<ListBoxItem>().First(temp => temp.Name == "item1");
If you need to find content, use this:
var item1Task = List.Items.Cast<ListBoxItem>().First(temp => temp.Content == "Item1");
You should give more attention to case sensitivity when comparing strings. Also there is no need to use Where and First together, because First already has conditional version.

How to Clone a whole grid of Controls?

I have the following code and basically what i am not able to figure out is how to clone the whole grid and make a blank copy of them side by side.... for a clear understanding this is something to do with hospital application and the grid is related to a pregnancy so when said 'ADD CHILD' button a whole new grid should be created during run time, thanks for the help below is a link that might help people cause i tried it but not sure how to display it
How can you clone a WPF object?
You should put the object you are want to "clone" in a DataTemplate and reference this template from an ItemsControl, then when you need another grid add another item to the items control (or even better to the list the control is bound to) and the ItemsControl will create a new grid and bind it the new object.
For an example take a look at this post on my blog.
Here is an example for this application (I left only the relevant parts and I didn't test it, so there are probably some typos there):
<Window ... >
<Window.Resources>
<DataTemplate x:Key="ChildTemplate">
<Grid>
...
<TextBlock Text="Delivery Date:" Grid.Column="0" Grid.Row="0"/>
<TextBox Text="{Binding DeliveryDate}" Grid.Column="1" Grid.Row="0"/>
<TextBlock Text="Delivery Time:" Grid.Column="0" Grid.Row="1"/>
<TextBox Text="{Binding DeliveryTime}" Grid.Column="1" Grid.Row="1"/>
...
</Grid>
</DataTemplate>
</Window.Resources>
...
<Button Content="AddChild" Click="AddChildClick"/>
...
<ScrollViewer>
<ItemsControl ItemsSource="{Binding AllChildren}" ItemsTemplate="{StaticResource ChildTemplate}">
<ItemsControl.PanelTemplate>
<ItemsPanelTemplate><StackPanel Orientation="Horizontal"/></ItemPanelTemplate>
<ItemsControl.PanelTemplate>
</ScrollViewer>
...
</Window>
And in cs:
Set an object with all the form data as the Window's DataContext. I'll call this class PostDelveryData.
Create another class with the repeating data. I'll call it ChildDeliveryData.
Add a property of type ObservableCollection<ChildDeliveryData> called AllChildren to PostDeliveryData; it's important it'll be ObservableCollection and not any other type of collection.
Now, for the magic:
private void AddChildClick(object sender, RoutedEvetnArgs e)
{
((PostDeliveryData)DataContext).AllChildren.Add(new ChildDeliveryData());
}
And when you add the new item to the list another copy of the entire data template will be added.
I'm not sure that you're using the correct approach here. I would approach the problem by creating a "ChildGridControl" with a Child property, and let the Child property handle the databinding. Adding a new child to the GUI would involve creating a new instance of the ChildGridControl.
If I am understanding correctly, you should create a UserControl, which wraps your Grid and subsequent controls inside. And use this User control anywhere you wanted to replicate that UI.

Resources