I have a listbox in Wpf. I directly add items to this listbox. I added a clear button which runs the items.clear() command and it deletes all the items. However when restarting the app, all the items are back in the list! Are they stored somewhere and if so, how can I disable this?
Another question: Can a datatemplate be applied to a listbox in which items are directly added and not bound?
Code example:
<ListBox x:Name="Results" Margin="222,31,8,44" />
Midiinput.ChannelMessageReceived += delegate(object sender, ChannelMessageEventArgs e)
{
string result = e.Message.MessageType.ToString() + " " + e.Message.Command.ToString() + " " + e.Message.Data1.ToString() + " " + e.Message.Data2.ToString();
Results.Items.Add(result); Clearbutton.IsEnabled = true;
};
the listbox items is not persisted (as any other property in fact).
How do you populate your listbox ? is there any backend storage ?
I guess that your application simply have some items declared in the markup and when the application starts, the list items are parsed.
If the items return when you restart your app, I would think one of the following is true:
1) You are populating the listbox at DESIGN time by using the Items property and entering strings into the collection by hand.
2) You are running code that is populating the list from a storage location, either in the code or in a file.
-C
For your second question
Can a datatemplate be applied to a
listbox in which items are directly
added and not bound?
The ItemTemplate of a ListBox is copied to the ContentTemplate of a ListBoxItem during UI generation. However, when adding the ListBoxItems directly, ItemTemplate is ignored for items already of the ItemsControl's container type (ListBoxItem). So you'll have to use the ContentTemplate of ListBoxItem instead. Here's an example
<ListBox>
<ListBox.ItemContainerStyle>
<Style TargetType="ListBoxItem">
<Setter Property="ContentTemplate">
<Setter.Value>
<DataTemplate>
<TextBlock Foreground="Green" Text="{Binding}"/>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</ListBox.ItemContainerStyle>
<ListBoxItem Content="Item 1"/>
<ListBoxItem Content="Item 2"/>
<ListBoxItem Content="Item 3"/>
</ListBox>
For your problem with items that are re-appearing when re-starting your app:
I'd try to find all the places where you add items to your ListBox in code-behind (search for Results.Items.Add). Add a breakpoint to each of those places to see in the debugger when you're adding them. The ListBox itself won't have any memory of how it looked when it closed unless you implement this.
If this doesn't work, try to change the name of your ListBox to ResultsTest or something, compile and fix all the errors you get by commenting out all the code that is referencing the ListBox and run the program again. This should leave you with no items in the ListBox. After this, start to uncomment your code, step by step until you find the place of the error.
Related
I have set up a ListView to manage tabs on my app. So users can drag and drop tabs to new instances of the app and reorder tabs. All goes great until I set up a ControlTemplate for the Itens at the ListView.
I used ControlTemplate because when I add a ListView.ItemTemplate directly it won`t work (it does not affect the listview item). So before adding a new item I use this:
tab.Template = (ControlTemplate)this.Resources["listViewItemTemplate"];
So the tabs is looking great, but all the functions that used to work (like drag and drop) isn't working anymore. When I remove the ControlTemplate the ListView show only one little string.
This is what is happening
My ListView Code:
<ListView x:Name="TabsListView"
CanDragItems="True"
AllowDrop="True"
DragEnter="TabsListView_DragEnter"
Drop="TabsListView_Drop"
ScrollViewer.VerticalScrollBarVisibility="Disabled
CanReorderItems="True"
DragItemsStarting="TabsListView_DragItemsStarting"
ItemClick="TabsListView_ItemClick" >
I know for sure that the issue isn't on the drag and drop method.
To create a new item I just use Add. Of course I checked every single item inside ControlTemplate to know that it is not blocking anything.
The control template is:
<ControlTemplate x:Key="listViewItemTemplate" >
<ListViewItem>
<Grid Tapped="Grid_Tapped" Width="180" Margin="-12,-12,-12,0">
<TextBlock Text="{Binding Name}" Margin="5,6,0,0"></TextBlock>
</Grid>
</ListViewItem>
</ControlTemplate>
The reason is that you are using ControlTemplate and embedding a ListViewItem in there. ListViewItems are created automatically by the control to contain the items, and ItemTemplate is a template of the contents of this container. So it should look like this:
<DataTemplate x:Key="listViewItemTemplate" >
<Grid Tapped="Grid_Tapped" Width="180" Margin="-12,-12,-12,0">
<TextBlock Text="{Binding Name}" Margin="5,6,0,0"></TextBlock>
</Grid>
</ControlTemplate>
<ListView ...
ItemTemplate="{ThemeResource listViewItemTemplate}">
...
</ListView>
Furthermore, if you want to style the ListViewItem (the container), you can create a style with TargetType="ListViewItem" and set it as ListView.ItemContainerStyle.
<Style TargetType="ListViewItem" x:Key="TabListViewItemContainerStyle">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListViewItem">
<Grid x:Name="ContentBorder">
... some template with ContentPresenter:
<ContentPresenter x:Name="ContentPresenter" ... />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ListView ...
ItemContainerStyle="{StaticResource TabListViewItemContainerStyle}">
...
</ListView>
You can find the default style in: C:\Program Files (x86)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.15063.0\Generic
Update
I have checked the code and found the problem. What you are actually doing is adding ListViewItems directly in code:
ListViewItem tab = new ListViewItem();
DataModel model = new DataModel();
model.Name = $"NewTab {counter}";
tab.DataContext = model;
tab.Content = model.ToString();
TabsListView.Items.Add(tab);
Unfortunately this does not work, because when ListView sees an item that is of type ListViewItem, it just adds it to the list, but ItemTemplate doesn't apply to it, because that is applied only when the items are not UIElement but rather a custom class. If you use DataModel directly, ItemTemplate will be applied as expected:
DataModel model = new DataModel();
model.Name = $"NewTab {counter}";
TabsListView.Items.Add(model);
ListView encounters DataModel, knows that just a class not a UIElement, so it internally creates a ListViewItem, applies ItemContainerStyle to it and then uses the ItemTemplate to create the UI that will be displayed inside the ListViewItem's ContentPresenter. It is better to use this approach as it gives you better decoupling as you don't have to create UI-based classes in the code-behind and you get more control (as you can change both the style of the item as well as the container).
I'm trying to highlight part of text in a textblock from a listbox datatemplate which in turn is bounded to a property of a custom class by using a textbox to search the list for input text. But the problem is that only part of the items are highlighting (most of the ones visible) but when i maximize the window and try to input another character then suddenly all of them gets highlighted my guess where the problem might be is in this piece of code:
ListBoxItem listboxItemFound= (ListBoxItem)this.listBox1.ItemContainerGenerator.ContainerFromItem(TItem);
Since this method is returning a null when the items are not visible but the items are currently in the listbox. Somehow I guess the items listboxItem instances are not yet created until you scroll down or maximize to view more items.
XAML DataTemplate:
<DataTemplate>
<Grid Name="gridOfListbox" Height="25" Margin="0,2">
<DockPanel Name="dockpanelWithTxtBlock">
<TextBlock Name="textbloxk" DockPanel.Dock="Left" FontSize="15" TextAlignment="Center">
<Run Text="" /><Run Background="Yellow" Text="" /><Run Text="{Binding ProductID}" />
</TextBlock>
</DockPanel>
</Grid>
</DataTemplate>
If more code is needed just let me know.
Any help would be greatly appreciated!!
Also if there is any other better way of finding the listboxItem bounded to the custom Item just let me know. Thank you very much!
[Pic of problem] http://i.stack.imgur.com/HViag.png
One way to fix this is to set VirtualizingStackPanel.IsVirtualizing to false for your ListBox. This will cause all of the items to be created right away. The downside to this is if your ListBox has many items, your program will use more memory (since more items will be created), and could potentially run slower depending on the number of items.
A better solution to consider would be to have multiple DataTemplates for this - one without the highlight, and one with. You can set a DataTemplateSelector for your ListBox (using the ItemTemplateSelector property). The selector can choose which template to use based on if the item matches the search term or not.
The tricky part would be writing the template with the highlighted text. You could probably achieve that by having properties on the object the ListBoxItem is bound to for the text before the highlighted text, the highlighted text, and then the remaining text.
I have a strange issue as in combobox style when i made virutalzation true, i have a checked Combobox to check all the checkboxes in the combobox items.
in ItemsPanel property, i've made some custom styling with VirtualizingStackPanel as below
<Setter Property="ItemsPanel">
<Setter.Value>
<ItemsPanelTemplate>
<VirtualizingStackPanel IsItemsHost="True"
IsVirtualizing="True"
VirtualizationMode="Recycling" />
</ItemsPanelTemplate>
</Setter.Value>
</Setter>
But before and after scrolling the view, it differs.
For e.g., here i try to get very first item of the combobox after scrolling down the items panel as
ComboBoxItem cmbItem = ComboBox1.ItemContainerGenerator.ContainerFromItem(ComboBox1.Items[0]) as ComboBoxItem;
But, i get nothing but NULL here.
So is there anyway we can achieve this using virutalization? (I know it's already virtualizing!). Or we should get rid virtualization and think of another solution. We saw that without virutalization property the combobox loads very slow.
Any thoughts?
Thanks in advance! :)
The NULL is directly a result of virtualization. It is the ComboBoxItem instances that are virtualized/recycled. Only the visible items from your collection will have a ComboBoxItem assigned, so if you ask for the ComboBoxItem on any item that is not visible, you will get back a NULL.
I'm new to WPF and I have this ListBox which I want to instantiate with a specific ListBoxItem, so the user knows what to do with the ListBox.
<ListBox Name="DbListBox"
Grid.Column="3"
HorizontalAlignment="Left"
Height="246"
Margin="0,99,0,0"
Grid.Row="1"
VerticalAlignment="Top"
Width="211"
SelectionMode="Single"
SelectedItem="{Binding Path=selectedDB,Mode=TwoWay}"
AllowDrop="True"
Drop="DbListBox_Drop">
<ListBoxItem Name="ListBoxItem" FontStyle="Italic">Drag .db file here or add below</ListBoxItem>
</ListBox>
Then I have some code which adds a collection of items to the ItemsSource of this ListBox, but I can't do that since the ItemsSource is not empty
DbListBox.ItemsSource = DbCollection;
My question is, how can I start up the ListBox with the item inserted first, and then when DbCollection is added to it, it simply overwrites the first ListBoxItem?
When using WPF properly, we'd normally have something like this, where a collection property would be data bound to the ListBox.ItemsSource property:
<ListBox ItemsSource="{Binding SomeCollectionProperty}" />
Once we have this XAML, we don't need to touch the ListBox again, as we can add or remove items from the data bound collection and they will magically appear (or disappear) from the ListBox:
SomeCollectionProperty.Add(new SomeDataType());
SomeCollectionProperty.Remove(someItemFromCollection);
The SomeDataType used here is up to you... it depends on what you want to display in your items. If it was just a plain string for example, then you could simply do this to add your initial item into the collection:
SomeCollectionProperty.Add("Drag .db file here or add below");
If you wanted to make that item look different to the others then you'd need to data bind a custom class that has a Text property and FontStyle property for example. You could data bind these properties in a DataTemplate to design each item to be exactly as you want it. However, that's a completely different question.
To find out more about these things, you can read the Data Binding Overview and Data Templating Overview page on MSDN.
I've looked around, found some things and now stuck on a combobox with two columns displayed in the drop-down area. I have a xaml themes available and the combobox "Style" is defined and works well throughout as expected, so that part is ok.
Now, I have a combobox that I need to have display two values, think of it as State Abbreviation and State Name for the drop-down, coming from a DataTable.DefaultView binding source for the items.
If I have
<my:cboStates TextSearch.TextPath="StateAbbrev">
<ComboBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal" TextSearch.Text="{Binding Path=StateAbbrev}">
<TextBlock Text="{Binding Path=StateAbbrev}"/>
<TextBlock Text="{Binding Path=FullStateName}" Margin="10 0"/>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</my:cboStates>
this works. Now, how/where I'm stuck... Now, I want this same functionality on say 5 different forms and all to have the same content displayed, and if ever changed (not this, but for other multi-column comboboxes), I don't want to have to keep putting this directly in the form's XAML file.
I was hoping to put into a Theme's Resource Dictionary file and just keep reusing that "style" over and over. Makes sense. However, when I do, and the binding is to the data table, the only results I get when trying to do as a Style is the dropdown shows values of
System.Data.DataRowView
System.Data.DataRowView
System.Data.DataRowView
System.Data.DataRowView
instead of the actual 2 columns.
Here is what I have in the "theme" resource dictionary.
<DataTemplate x:Key="myStateComboTemplate" >
<StackPanel Orientation="Horizontal" >
<TextBlock Text="{Binding Path=StateAbbrev}"/>
<TextBlock Text="{Binding Path=FullStateName}"/>
</StackPanel>
</DataTemplate>
<Style x:Key="StyleMyStatesCombobox" TargetType="{x:Type ComboBox}"
BasedOn="{StaticResource MyOtherWorkingComboBoxStyle}" >
<Setter Property="TextSearch.TextPath" Value="{Binding Path=StateAbbrev}" />
<Setter Property="ItemTemplate" Value="{StaticResource myStateComboTemplate}" />
</Style>
So, If I have TWO instances my "cboStates" class created on the form, and set one to the explicit styling listed first, and the SECOND based on the "Style" setting, the second one fails by only showing the repeated System.Data.DataRowView entries, not the actual data content.
What am I missing.
So, to clarify what I'm looking for...
States... ex data
AL Alabama
AK Alaska
AZ Arizona
AR Arkansas
CA California
CO Colorado
CT Connecticut
DE Delaware
I want the combobox to be displaying the abbreviated
AL, AK, AZ, etc and narrower combobox. This will ALSO be the "SelectedValue" upon return.
The actual Dropdown would present the data as listed above showing BOTH the abbreviation AND the long description of the state.
Sample of desired combobox
FINALLY got it working... and for those attempting similar. Since I was trying to have a standard "class" instance that could be used throughout, but not wanting to explicitly hard-reference the XAML in each page, part of the styling had to be handled during the actual in-code class instance.
Since I don't exactly know how/when where .net framework builds out all its controls, style assigments, etc, I was getting frustrated that it would work if direct from xaml, but fail when in code. So, I ended up FORCING the item template AND TextSearch.TextPath values in the code. Here's a short snippet of the class
public class myStatesCombo : ComboBox
{
public myStatesCombo()
{
Loaded += myAfterLoaded;
}
protected static DataTable myTableOfStates;
public void myAfterLoaded()
{
if( myTableOfStates == null )
myTableOfStates = new DataTable();
CallProcedureToPopulateStates( myTableOfStates );
ItemsSource = myTableOfStates.DefaultView;
// AFTER the object is created, and all default styles attempted to be set,
// FORCE looking for the resource of the "DataTemplate" in the themes.xaml file
object tryFindObj = TryFindResource("myStateComboTemplate" );
if( tryFindObj is DataTemplate )
ItemTemplate = (DataTemplate)tryFindObj;
// NOW, the CRITICAL component missed in the source code
TextSearch.SetTextPath( this, "StateAbbrev" );
}
}
Now, a special note. In the routine I used to populate the DataTable, I pre-check if the table exists or not. First time in, I create the table. If I need to re-populate it, if I just keep doing a "new DataTable" each time, it blows away the data/item template bindings. To PREVENT that, I would then do
if( myTableOfStates.Rows.Count > 0 )
myTableOfStates.Rows.Clear();
THEN, I call my
Sqlexecute call to query from the database (DataAdapter) and Fill() the datatable.
So, now all appears to be populated correctly, bindings for display and textsearch complete and ready to go.