How to add ListBox Items in WPF? - wpf

how do I add items to a listbox control by using < ListBox.ItemTemplate> ?
here is the xaml part:
<ListBox HorizontalAlignment="Stretch" Name="ListBox1" VerticalAlignment="Stretch" Margin="0,20">
<ListBox.ItemTemplate>
<DataTemplate>
<Label Content="???????" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and here is the code for adding items:
private sub Button1_Click() Handles Button1.Click
ListBox1.Items.Add("Hello World")
End Sub
if I click on the buton1, a list item will be added with "??????" - I need to replace the "?????" with {Binding} or something so it can get the value correct value from button1_click ("Hello World")

Yes, just replace it with {Binding}, that alone should do it.
({Binding} binds to the DataContext and the DataContext of the ItemTemplate will be the respective item, if the item is a simple string as in your example that will do, if it is a complex data object you will want to specify a Path to bind to a property of the item)

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#.

Binding datacontext of usercontrol to mainwindow

I have a UserControl positioned inside of the MainWindow. The UserControl runs a query and populates certain TextBlocks within it. I also want to populate TextBlock in the MainWindow from the same returned data.
How do I bind the MainWindow data to the UserControl? I have tried this:
<MainWindow DataContext="{Binding Path=DataContext, ElementName=UserControlName}">
Any help would be appreciated. Thanks!
Here is a simple working example.
The UserControl XAML contains just a two-way bound text box. The relative source stuff is more verbose than you need, you could have a data context set above that, but it's just to make it clear where the property is coming from:
<TextBox x:Name="ucTextBox"
Text="{Binding Path=UcText,
RelativeSource={RelativeSource AncestorType={x:Type local:UserControl1}},
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}" />
User Control code behind declares either a dependency property or, as shown here, a regular property implementing INotifyPropertyChanged:
private string _ucText;
public string UcText
{
get { return _ucText; }
set
{
_ucText = value;
OnPropertyChanged("UcText");
}
}
The MainWindow XAML then sets it's own text block to the text property from the textbox in the user control, like so:
<StackPanel Orientation="Vertical">
<TextBlock Text="{Binding Path=UcText, ElementName=uc1}"/>
<local:UserControl1 x:Name="uc1" />
</StackPanel>
Nothing extra is required in the MainWindow codebehind.
What this results in is a text box (in the user control) which - as you type inside it - updates the text block on the main window.

wpf binding to string

I have a Movie class with a Dim _characters = New ObservableCollection(of String)
Characters is the associated property to get and set
How can i get characters to show up in the listBox using Binding?
So far i have the following, this isn't working as i don't know what to put instead of ToString.
<ListBox Name="cList" ItemsSource="{Binding Characters}">
<ItemsControl >
<ItemsControl.ItemTemplate >
<DataTemplate >
<TextBox Text="{Binding ToString}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ListBox>
I want them to be editable, hence a textbox.
i tried to bind Characters to TextBox directly, even that didn't work.
Edit :
in the code i have parentGrid1.DataContext = me.movies where
parent grid holds movies.
For those who are experiencing the exception
... binding requires path or xpath ...
You can bind the object directly this way:
<Label Content="{Binding .}" />
Change your TextBox binding to the following. I think it should work:
<TextBox Text="{Binding}"/>
This loads the item itself instead of a property or method output. Since the item is a string it should bind to the strings value.
You cannot perform two-way binding to ObservableCollection<string>. In order to make the strings editable you have to create a class with a string get/set property as the following class Foo:
public class Foo
{
string _text;
public Foo(string text)
{
_text = text;
}
public string Text
{
get { return _text; }
set { _text = value; }
}
}
Your Characters should then be of type ObservableCollection<Foo> and your XAML should be changed so that the textboxes are binding to Foo.Text:
<ListBox ItemsSource="{Binding Characters}" >
<ListBox.ItemTemplate >
<DataTemplate >
<TextBox Text="{Binding Text}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Just remove the ToString portion of the code.
Currently you are telling the program that you want to bind to an object called ToString
I take it that Characters is a public property. Debug and be sure that get is being called for Characters. If you have a the datacontext of the page/window to Movies then you need ItemsSource on the ListBox to be {Binding Path=Characters}

Populate ComboBox based on another ComboBox using XAML

I have two ComboBoxes
<ComboBox Name="cmbMake" DisplayMemberPath="MakeName" SelectedValuePath="MakeID"/>
<ComboBox Name="cmbModel" DisplayMemberPath="ModelName"/>
I use LINQ-to-Entities to populate the cmbGroup ComboBox
Dim db as myDataEntity
cmbGroup.ItemsSource = db.Makes
How do I populate my second ComboBox (cmbModels) based on the selection of the first ComboBox (cmbMake) using XAML so that whatever I select in the first ComboBox automatically filters the ItemsSource in the second ComboBox?
Is this even possible?
I am posting the full solution here
XAML
<ComboBox Name="cmbMake" DisplayMemberPath="MakeName" SelectedValuePath="MakeID" Width="200"/>
<ComboBox Name="cmbModel" DisplayMemberPath="ModelName" DataContext="{Binding SelectedItem, ElementName=cmbMake}" Width="200"/>
CODE-BEHIND
Private Sub cmbMake_SelectionChanged(ByVal sender As System.Object, ByVal e As System.Windows.Controls.SelectionChangedEventArgs) Handles cmbMake.SelectionChanged
Dim myItem = From m In myModel
Where m.MakeID = cmbMake.SelectedValue
cmbModel.ItemsSource = myItem
End Sub
Whenever the value is changed in the cmbModel ComboBox it will use LINQ to reset the ItemsSource of the cmbModel ComboBox.
Many Thanks to #XAMeLi for the helping hand!
If your data is hierarchical, where each item in db.Makes holds a list of the Models (and lets say this list is in a property called MyModelsList), then:
<ComboBox Name="cmbMake" DisplayMemberPath="MakeName" SelectedValuePath="MakeID"/>
<ComboBox Name="cmbModel" DisplayMemberPath="ModelName" DataContext="{Binding SelectedItem, ElementName=cmbMake}" ItemsSource="{Binding MyModelsList}"/>
It should be possible to use a converter to filter the items, for that you can employ a MultiBinding to get the values for the items and the selection in the other box in.
Would look something like this:
<ComboBox Name="cmbModel" DisplayMemberPath="ModelName">
<ComboBox.ItemsSource>
<MutliBinding>
<MultiBinding.Converter>
<vc:MyFilterConverter/>
</MultiBinding.Converter>
<Binding Path="Items"/> <!-- This should bind to your complete items-list -->
<Binding Path="SelectedValue" ElementName="cmbMake"/>
</MutliBinding>
</ComboBox.ItemsSource>
</ComboBox>
The converter needs to implement IMultiValueConverter.

XAML Code for TREEVIEW

Following is the code I wrote for generating treeview hierarchy,
For Each k As KeyValuePair(Of String, GenreSet) In GenreSetDictionary
Dim t As New TreeNodeSet
t.Genre = True
t.Imagepath = k.Value.IconPath
t.Namee = k.Key
Dim pnode As New TreeViewItem
pnode.DataContext = t
pnode.Visibility = True
For Each z As DatabaseDearDataSet.DiskListRow In adpt.GetDataByGenre(t.Namee)
Dim tt As New TreeNodeSet
tt.Genre = False
tt.Imagepath = IconDictionary(z.DiskIcon).IconPath
tt.Namee = z.DiskName
Dim cnode As New TreeViewItem
cnode.DataContext = tt
pnode.Items.Add(cnode)
Next
DisksTreeView1.Items.Add(pnode)
Next
Following is the code I have used in XAML:
<TreeView Height="211" HorizontalAlignment="Left" Margin="19,15,0,0" Name="TreeView1" VerticalAlignment="Top" Width="346">
<TreeView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<Image Source="{Binding ImagePath}" Width="32" Height="32"/>
<TextBlock Text="{Binding Namee}" VerticalAlignment="Center" HorizontalAlignment="Center" />
</StackPanel>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
However, I was not able to get that work, could you please tell me where my XAML went wrong please.
I see few minor mismatches here: the name of the TreeView control (TreeView1 or DisksTreeView1) and the ImagePath property (or Imagepath, c# is sensible to the register of variables).
But the main reason of the incorrect behavior is that the ItemTemplate property is applied to the ItemsSource property, not to the Items property.
Here are two possible ways to correct the code:
1) Fixeing of the data class, item template and binding to the ItemsSource
Create the myObservableCollection private field of the type ObservableCollection(Of TreeNodeSet).
Add to the constructor the line DisksTreeView1.ItemsSource = myObservableCollection
Change the line DisksTreeView1.Items.Add(pnode) to the line myObservableCollection.Add(t).
Add the Disks property to the TreeNodeSet class (the type is ObservableCollection too)
In the xaml replace the line with DataTemplate to the line <HierarchicalDataTemplate ItemsSource="{Binding Disks}"
Change the line pnode.Items.Add(cnode) to the line t.Disks.Add(tt).
2) Using the HeaderTemplate property instead of the ItemTemplate property.
At first, move the DataTemplate to resources and add some key. Then add a similar code near each TreeViewItem in the code-behind:
Dim pnode As New TreeViewItem
pnode.DataContext = t
pnode.Header = t
pnode.HeaderTemplate = Resources("someKey")

Resources