I'm creating an application where we open a new window that has options to choose from in a listview, we extract one of the items in the listview and pass it on to another listview on a different window. I can populate the listview on the main window and can verify that the items are getting added during debug with an .items.count but they aren't getting added realtime. If I do a .show to open another instance of the window I see the items there but since I'm not planning on closing the main window out, how do you 'refresh' the listview to show the options that are clearly there?
I've read that if the listview is binded to a table, you can just re-bind the data to refresh it but since this listview isn't binded to anything and is just adding items to it with the .add function, I'm not sure how to 'refresh' it to show the values.
Edit:
Here is my code currently,
Public Class ObservableColl
Public Structure Steps
Private _Steps As List(Of String)
Property Steps() As List(Of String)
Get
Return _Steps
End Get
Set(ByVal value As List(Of String))
_Steps = value
End Set
End Property
End Structure
Dim songs As New ObservableCollection(Of Steps)
End Class
Private Sub CloseForm()
Dim stepArr As String() = Split(sCommon.presetSteps, ",")
Dim entries As New List(Of String)
For i = 0 To stepArr.Count - 1
entries.Add(stepArr(i))
Next
sCommon.form1.entries.Add(New Steps With {.Steps = entries}) 'Add list entries to the observableCollection
sCommon.form1.lvwCurrentSteps.DataContext = entries 'Assign the DataContext property to the observableCollection
If Not PresetList Is Nothing Then
Me.Close() 'Close this form and focus returns to the main window
End If
End Sub
<ListView Margin="49,23,0,33" HorizontalAlignment="Left" Width="230" Name="lvwCurrentSteps" SelectionMode="Multiple" ItemsSource="{Binding}" >
<ListView.ItemContainerStyle>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="BorderBrush" Value="LightGray" />
<Setter Property="BorderThickness" Value="0,0,0,1" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView x:Name="myGridView">
<GridViewColumn Width="220" Header="Steps to run" DisplayMemberBinding="{Binding Path=Steps}">
</GridViewColumn>
</GridView>
</ListView.View>
</ListView>
I can validate that there are items in the listview with this method but the listview doesn't 'refresh' with these values when focus returns to the original window.
?sCommon.form1.lvwCurrentSteps.Items.Count
3
It's hard to tell from just this snippet but it looks like your logic is backwards in these two lines:
sCommon.form1.entries.Add(New Steps With {.Steps = entries}) 'Add list entries to the observableCollection
sCommon.form1.lvwCurrentSteps.DataContext = entries 'Assign the DataContext property to the observableCollection
I would expect that the DataContext you would want to use would be the collection you just added to, not the local variable that was what you added (which is a List Of String and so has no Steps property to support your DisplayMemberBinding). It also seems a little strange the way that the ObservableColl class is set up, with an ObservableCollection of objects containing a List.
I'm not sure what's happening with the count but you should validate that your basic logic is really doing what you mean it to.
Related
I have a WPF form which includes a ListView and the following bindings:
<ListView ItemsSource="{Binding SelectableItems}" SelectionMode="Extended">
<ListView.ItemContainerStyle>
<Style TargetType="ListViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
</Style>
</ListView.ItemContainerStyle>
<ListView.View>
<GridView>
<GridViewColumn Header="Data From" DisplayMemberBinding="{Binding Started}" Width="150" />
<GridViewColumn Header="Data To" DisplayMemberBinding="{Binding Finished}" Width="150" />
</GridView>
</ListView.View>
</ListView>
The ViewModel's SelectableItems property returns an ObservableCollection of SelectableObject items where the SelectableObject inherits from ListViewItem.
The challenge we are having is as follows. When the view is initialized, one or more items in the SelectableItems collection may have their IsSelected property set to true. When the view is shown, visually each item appears to be in the correct selection state. Unfortunately, it appears that the "current selection" information is not being fully communicated to the ListView itself. The expected operation upon performing a simple single click on a non-selected item is that any currently selected items are deselected and the single clicked item then becomes the only item with IsSelected set to true. This does not appear to be happening. Instead the newly clicked item is added to the set of previously clicked items... but only the first time. All subsequent single click operations operate as expected.
Is there something else that needs to be done during initialization to correctly initialize the ListView's expectations concerning the currently selected items?
Basically, the ListView uses the SelectedItems property to keep track of the extended selection. When your collection is bound to the control, it has no way of knowing / updating the SelectedItems collection, and therefore doesn't know that there are objects selected. Unfortunately, the SelectedItems property is not bindable.
I have in the past created Attached Properties to 'wrap' the SelectedItems collection with a bindable property, but it is cumbersome, and I am fairly sure that my implementation might cause leaks.
Alternatively, you can use some minor code-behind / attached property to set the SelectedItems property when the ItemsSource changes:
Edit: Changed to Attached Property
public class Bindable
{
public static readonly DependencyProperty InitializeSelectedItemsProperty = DependencyProperty.RegisterAttached(
"InitializeSelectedItems", typeof(bool), typeof(Bindable), new PropertyMetadata(default(bool), InitializeSelectedItemsChanged));
private static void InitializeSelectedItemsChanged(DependencyObject depObject, DependencyPropertyChangedEventArgs args)
{
var listView = depObject as ListBox;
if (listView != null && (bool)args.NewValue)
{
TypeDescriptor.GetProperties(listView)["ItemsSource"]
.AddValueChanged(listView, new EventHandler(ListViewItemsSourceChanged));
}
}
private static void ListViewItemsSourceChanged(object sender, EventArgs eventArgs)
{
var listView = sender as ListBox;
listView.SelectedItems.Clear();
foreach (var item in listView.ItemsSource.OfType<ISelectable>().Where(i => i.IsSelected))
{
listView.SelectedItems.Add(item);
}
}
}
<ListView ItemsSource="{Binding SelectableItems}" Bindable.InitializeSelectedItems="True" SelectionMode="Extended" ...>
A solution will be to store the selected items in a list, or better in a dictionnary, if your listview is a list of items which could be identified as a pair of Key/Value.
Then when your list is updated by the binding, you should iterate through your dictionary and test if a couple key/value exist in the listview you then if true set is property to Selected.
It's a way.
Is it possible to display something in my treeview at design time and what would be the best way to achieve it?
Runtime works perfectly well and does display the data I want. However, I would like to have a more convenient display when designing than an empty area where the treeview is.
Thank you.
The treeview looks like this:
<TreeView x:Name="tvConfig" Width="400" Height="300" >
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type demo:TvItemsSource}" ItemsSource="{Binding Path=Items}">
<StackPanel Orientation="Horizontal">
<Image></Image>
<TextBlock Text="{Binding Name}"></TextBlock>
</StackPanel>
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
And make uses of this class:
Public Class TvItemsSource
Public Property Items() As New List(Of TvItemsSource)
Public Property Name() As String
Public Sub New(Name As String)
Me.Name = Name
End Sub
End Class
I generated a small runtime data using the following piece of code:
Private _source As TvItemsSource
Private Sub LoadData()
_source = New TvItemsSource("Root")
_source.Items.Add(New TvItemsSource("Item 1"))
_source.Items.Add(New TvItemsSource("Item 2 "))
Dim ParentItem1 = New TvItemsSource("Parent 1")
ParentItem1.Items.Add(New TvItemsSource("Enfant 1"))
Dim ParentItem2 = New TvItemsSource("Parent 2")
ParentItem2.Items.Add(New TvItemsSource("Enfant 2"))
ParentItem1.Items.Add(ParentItem2)
_source.Items.Add(ParentItem1)
tvConfig.ItemsSource = _source.Items
End sub
edit: In the xaml of the treeview, I added the xmlns namespace of the application to declare the data type as :
xmlns:demo="clr-namespace:demo"
I just found the ideal solution for my project by using this statement:
Dim IsDesignMode As Boolean = DesignerProperties.GetIsInDesignMode(New DependencyObject())
The IsDesignMode variable does return true and anything therefore a listbox items source binded on a "Content" property would display the "Design Mode" items in the designer and the "Production" items when ran.
If IsDesignMode Then
For I = 1 To 10
Content.Add("DesignMode " & I)
Next
Else
Content.Add("Production 1")
Content.Add("Production 2")
Content.Add("Production 3")
End If
There are other way to do it.
Relevant:
SO: What approaches are available to dummy design-time data in WPF
Also:
Simulating data in design mode in Microsoft Blend
I’m vb.net developer that’s new to WPF.
I’m converting a Windows Form app to WPF and I’m having a problem updating my listview items as I add them. I have a communication process within the app that connects to a server to send or download information to/from it using a tcp connection. This process updates a listview to show the status of the communication with the server. But the items are not displayed as I add them, instead they are all visible when the process finishes. I will like to refresh the items as they are added. In Windows Forms I used the code:
ListView1.EnsureVisible(ListView1.Items.Count - 1)
witch will ensure that the last item was visible in the listview. That code however does not works in WPF. Im looking for an alternative to achive this. Any suggestion is welcome even if it envolves using another element rather than a listview. Or if someone knows an alternative using C# it is also welcomed.
My VB code that updates my listview is the following:
Private Sub DebugMsg(ByVal Message As String, Optional ByVal NewLine As Boolean = True)
If NewLine = True Then
Dim item As New ListViewItem
item.Content = Message
Me.ListView1.Items.Add(item)
item = Nothing
ElseIf NewLine = False Then
If ListView1.Items.Count > 0 Then
ListView1.Items(ListView1.Items.Count - 1).Content = Message
Else
Dim item As New ListViewItem
item.Content = Message
Me.ListView1.Items.Add(item)
item = Nothing
End If
End If
Me.ListView1.Items.Refresh()
End Sub
This sub is used every time I want to update the listview to show the status of the communication.
My XAML Code is the following:
<ListView x:Name="ListView1" Margin="10,101,10,10" Background="#FF0C0C0C" Foreground="Cyan" FontSize="20" ItemsSource="{Binding}" >
<ListView.View>
<GridView>
<GridView.ColumnHeaderContextMenu>
<ContextMenu/>
</GridView.ColumnHeaderContextMenu>
<GridViewColumn Header="Status" Width="475"/>
</GridView>
</ListView.View>
Thanks in advanced!
I've encountered a problem when converting a WPF project from vs2008 to vs2010.
I have a DataGrid that contains a ListBox. Each ListBoxItem has a Label and a Button. After converting to vs2010 the button no longer renders but crashes the app as soon as it comes into view. (Ie. the app loads but when the ListBox is created I get a NullReferenceException. What does work though is to remove the click event from the button and then it renders fine :) Same type of setup with Button within ListBoxItem also works when not inside a DataGrid. The content of ListBox obviously is meant to be dynamic but when working with a static collection I get the same error. Also removing the CommandParam does not help at all. Any pointers most welcome.
Code:
<DataGrid x:Name="DgTest" AutoGenerateColumns="false">
<DataGrid.Columns>
<DataGridTemplateColumn>
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<StackPanel>
<ListBox ItemsSource="{Binding ItemList}">
<ListBox.ItemTemplate>
<DataTemplate >
<StackPanel Style="{StaticResource hzp}">
<Label />
<Button Click="Button_Click" Content="TestButton"/>
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</StackPanel>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
Code-behind:
Imports System.Collections.ObjectModel
Class MainWindow
Public TestList As New ObservableCollection(Of TestClass)
Private Sub MainWindow_Loaded(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
DgTest.ItemsSource = TestList
TestList.Add(New TestClass(0))
TestList.Add(New TestClass(1))
End Sub
Public Class TestClass
Private _ItemList As New List(Of String)
Private _id As Integer
Public Property ItemList() As List(Of String)
Get
Return _ItemList
End Get
Set(ByVal value As List(Of String))
_ItemList = value
End Set
End Property
Public Property Id() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
Public Sub New(ByVal id As Integer)
_ItemList.Add("String1")
_id = id
End Sub
End Class
Private Sub Button_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs)
End Sub
End Class
And in App Resources:
<Style TargetType="StackPanel" x:Key="hzp">
<Setter Property="Orientation" Value="Horizontal"/>
<Setter Property="Background" Value="Orange"/>
</Style>
Now here's the strange thing. If the Stackpanel Style is removed, the button will work. If the Click event for the button is removed, it will load normally.
Seems like your event handler is gone from the code-behind file, check that first. Comment if that is not the case.
I believe I have found the answer to my own question. In the ListBox bound to an ObservableCollection all Styles must be DynamicResource. Using StaticResource worked well in 3.5 but not 4! Took a few hours of randomly testing everything to find this. Case closed
I am novice in WPF. I have a wpftoolkit datagrid where i am using a combo box as datagridcombox column. I am using a observable collection of Codes for binding the combo box. Below is the collection and its class...
#Region "Class & Coll"
Public Class CodesColl
Inherits ObservableCollection(Of Codes)
End Class
Public Class Codes
Private pCode As String
Private pDescription As String
Public Sub New()
pCode = String.Empty
pDescription = String.Empty
End Sub
#End Region
#Region "Property"
Public Property fldCode() As String
Get
Return pCode
End Get
Set(ByVal value As String)
pCode = value
End Set
End Property
Public Property fldDescription() As String
Get
Return pDescription
End Get
Set(ByVal value As String)
pDescription = value
End Set
End Property
#End Region
End Class
Now what i want achieve is that i need to bind the collection with dropdown in the grid.In my grid i have two columns in first column i have to display the code (fldCode) , and on the selection of the code the next column of the same row will get populated with its description (fldDescription).
My Xaml is something like this:
<wpfkit:DataGrid Margin="3" Style="{DynamicResource SimpleDataGrid}" FontWeight="Normal"
MaxHeight="100" ItemsSource="{Binding Source={StaticResource odpExistingCodesColl}}"
AutoGenerateColumns="False" Name="dgCodes" VerticalScrollBarVisibility="Visible" >
<wpfkit:DataGrid.Columns>
<wpfkit:DataGridTemplateColumn IsReadOnly="True">
<wpfkit:DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<Image Style="{DynamicResource SimpleImageDelete}"/>
</DataTemplate>
</wpfkit:DataGridTemplateColumn.CellTemplate>
</wpfkit:DataGridTemplateColumn>
<wpfkit:DataGridComboBoxColumn Header="Code"
DisplayMemberPath="fldCode"
SelectedValueBinding="{Binding fldCodes.fldCode}"
SelectedValuePath="fldCode"
SelectedItemBinding="{Binding fldCodeList}"
Width="100" x:Name="cbTCodes" >
<wpfkit:DataGridComboBoxColumn.ElementStyle>
<Style TargetType="ComboBox">
<Setter Property="IsSynchronizedWithCurrentItem" Value="False" />
<Setter Property ="ItemsSource" Value="{Binding Path=odpCodesColl}"/>
</Style>
</wpfkit:DataGridComboBoxColumn.ElementStyle>
<wpfkit:DataGridComboBoxColumn.EditingElementStyle >
<Style TargetType="ComboBox">
<Setter Property ="ItemsSource" Value="{Binding Path=odpCodesColl}"/>
<Setter Property ="IsDropDownOpen" Value="True"/>
</Style>
</wpfkit:DataGridComboBoxColumn.EditingElementStyle>
</wpfkit:DataGridComboBoxColumn>
<wpfkit:DataGridTextColumn Width="375" Header="Description" x:Name="tbTCodeDescription" />
</wpfkit:DataGrid.Columns>
</wpfkit:DataGrid>
odpExistingCodesColl here is another collection through which i am binding the entire grid and is used for sending the code and its description to but i am facing following problems
Unable to display the codes in dropdown.
Somehow i manged to do so but it disappears after loosing focus from the combobox.
Unable to retrive the description on its selection change as , i am unable to find the event too.
So you guys are requested to help me out asap.. any help will be highly appreciated..
Thanks in Advance
Amit Ranjan
You can check on Vincent's blog for detail information about how to work with Wpf DataGrid(DataGridComboBoxColumn too).