ListBox showing one property in different rows - c#

I am using the following code for binding ListBox to a list i.e. List and set the binding Path=Name. But the list box shows just the one name with letter divided in rows. Like if a Name is JOHN, the list box row 1 shows "J", row 2 shows "O", row 3 shows "H", row 4 shows "N". Here's the code.
Xaml
<ListBox Height="Auto" ItemsSource="{Binding Path=Name}" HorizontalAlignment="Stretch" Margin="0,80,0,0" Name="ledgerListView" VerticalAlignment="Stretch" Width="200" KeyDown="ledgerListView_KeyDown" MouseDoubleClick="ledgerListView_MouseDoubleClick" IsSynchronizedWithCurrentItem="True" />
Code-Behind
List<Ledgers> ledgers = new List<Ledgers>();
ledgers = DAL_Ledgers.LoadLedgers();
this.DataContext = ledgers;

The ItemsSource property needs to be bound to the source collection that you want to generate the list box's items from. In this case that would just be the DataContext. To show the name for each item you can either apply a DataTemplate to the ItemTemplate property containing what you want to show for each item, or for a simple case like this just use the DisplayMemberPath to specify the Name property.
<ListBox ItemsSource="{Binding}" DisplayMemberPath="Name" x:Name="ledgerListView"/>

It looks like you're binding to the wrong thing... Does it work if you use:
<ListBox ItemsSource="{Binding}" ...>
<ListBox.ItemTemplate>
<DataTemplate>
<sdk:Label Content="{Binding Path=Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>

Also, you might want to use an ObservableList, otherwise changes in ledgers won't be taken into account.

Related

combobox and listview binding issue wpf c#

I have a combobox which displays listview on dropdown, I am following MVVM Pattern and i have also set the public property in my Viewmodel and it works fine when i am assigning it to the Label but for Combobox it doesn't seem to rely on my binding. i tried numerous ways but unable to find the issue.
XAML :
<ComboBox Name="SearchBox" IsEditable="True" Background="White" md:HintAssist.Hint="Search MUID" Grid.Column="1" Margin="5 0 0 0"
Grid.Row="0" Height="40" Width="400" HorizontalContentAlignment="Left" HorizontalAlignment="Left" SelectedItem="{Binding ElementName=lstview ,Path=SelectedItem}" >
<ComboBoxItem>
<ListView x:Name="lstview" ItemsSource="{Binding filterSW}"
SelectedItem="{Binding SelectedMU}"
Height="200" ScrollViewer.VerticalScrollBarVisibility="Visible">
<ListView.View>
<GridView>
<GridViewColumn Width="130" Header="Mu-ID" />
<GridViewColumn Width="130" Header="MU-Identifier" DisplayMemberBinding="{Binding MU_Identifier}" />
<GridViewColumn Width="130" Header="Status" DisplayMemberBinding="{Binding RequestType}" />
<GridViewColumn Width="130" Header="UniqueID" />
</GridView>
</ListView.View>
</ListView>
</ComboBoxItem>
</ComboBox>
This works fine for me when i am using the public property and accessing its element , i also tried setting text={Binding SelectedMU.MU_Identifier} and selectedvalue but its just not working.
<Label Grid.Column="3" HorizontalAlignment="Center" Background="GreenYellow" Content="{Binding SelectedMU.MU_Identifier}"></Label>
It looks like you're trying to show a multi-column list in your ComboBox dropdown instead of the standard list where each item shows just a text line.
To achieve this effect you've placed a ListView inside the dropdown.
Unfortunately, this is just not going to work.
Both ComboBox and ListView descend from Selector which is an abstraction that allows to select an item from a list. This limits the property SelectedItem to one of the items that are contained in the list. If you try to assign to this property any value that it not in the list, the assignment is not going to work and the property will retain the value it had before you did the assignment.
Now, the list could either be specified right inside XAML or provided as a binding to property ItemsSource. You do the binding correctly for the ListView. But for the ComboBox you don't specify that binding. Instead you specify exactly one item of type ComboBoxItem which contains the whole ListBox as its value. So the only value that could be successfully assigned to the SelectedItem property of the ComboBox is that single ComboBoxItem. But your binding is never going to assign that value, that's why the ComboBox never shows anything when closed.
When it's open it does show the single item which contain the ListView but this is just an optical effect. The data binging is not going to work. The reason why it works for the Label is because the Label is not constrained and can show anything that the ListView tells it to show.
You can synchronize the ListView and the ComboBox only when both controls have the same bindings for both ItemsSource and SelectedItem properties. But in this case you won't be able to place the ListView inside the dropdown.
The closest you can get to what you want is by customizing the ComboBox's template as described in https://zamjad.wordpress.com/2012/08/15/multi-columns-combo-box, for example. What this won't give you compared to ListView is the column headers. Also, the columns will be evenly spaced inside the dropdown but this is what you have in your ListView anyway.
If you want to auto-size them, you'd need to add Width="Auto" SharedSizeGroup="cN" to each ColumnDefinition where "cN" should have the column number instead of N to make them unique within the Grid and add Grid.IsSharedSizeScope="True" to the <ComboBox >
That's a lot of trouble for something that one would expect to be much simpler, but, unfortunately, you cannot place a ListView inside the ComboBox's template, that's a limitation of how the base class Selector works with its items list.
There are other options if you are open to consider 3rd party control libraries. I worked with Syncfusion, they have SfMultiColumnDropDown which does what you want. I'm pretty sure other popular libraries have similar controls as well.

ListView default selected items mvvm

I have ListView with dependency property for selecting more than one item. ListView sample code is here:
<ListView ItemsSource="{Binding Test}"
ItemTemplate="{StaticResource SomeTemplate}"
SelectionMode="Multiple"
multipleBind:SelectionChangedCommand.Command="{Binding SelectionChangedCommand}">
</ListView>
It works properly but what I want is pre-select default items and I don't know How do it. Is it a possible? Please give me some hit.
Thanks

DataGrid in DataGrid.RowDetailsTemplate with same ItemsSource (Current Item)

I have a problem with a DataGrid which is inside a DataGrid.RowDetailsTemplate.
I have an ObservableCollection Classes which has about 20 properties but no other collections. I want to split them for better viewing. The first DataGrid called "mainGrid" should show the first 10 properties...
Then I have defined DataGrid.RowDetailsTemplate to show the rest if the user click on the row. But it doesn't work. The second DataGrid which is inside the RowDetailsTemplate
<Dgv:DataGrid.RowDetailsTemplate>
<DataTemplate>
<Dgv:DataGrid x:Name="dgvRowDetails">
<Dgv:DataGrid.Columns>
<Dgv:DataGridTextColumn Header="Parameter 1"
Binding="{Binding Parameter1}" />
<Dgv:DataGridTextColumn Header="Parameter 2"
Binding="{Binding Parameter2}" />
</Dgv:DataGrid.Columns>
</Dgv:DataGrid>
</DataTemplate>
</Dgv:DataGrid.RowDetailsTemplate>
It displays only the headers in the RowDetails (when I click on a row) but no content.
The output tab doesn't show any binding errors.
I think I need a ItemsSource, too but I don't know how to realise it because the other properties refer to the current or selectedItem.
But this e.g works fine:
<Dgv:DataGrid.RowDetailsTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text = "{Binding Parameter1}" />
<TextBlock Text = "{Binding Parameter2}" />
</StackPanel>
</DataTemplate>
</Dgv:DataGrid.RowDetailsTemplate>
But I really want a DataGrid because of Headers etc...
I am using .NET 3.5
Many Thanks in advance!
You can't really do this. ItemsSource expects to be an IEnumerable and what you want is to give it a single item.
This is sort of a hacky way to accomplish it. In your object's class that you bind the outer datagrid's ItemSource to define a new property that returns itself in the form of some IEnumerable
public class MyClass
{
public IEnumerable<MyClass> AsIEnumerable
{
get
{
yield return this;
}
}
}
XAML
//Inner datagrid
//Datacontext is MyClass object
<Dgv:DataGrid x:Name="dgvRowDetails"
AutoGenerateColumns="False"
ItemsSource="{Binding AsIEnumerable}">
<Dgv:DataGrid.Columns>
<Dgv:DataGridTextColumn Header="Parameter 1"
Binding="{Binding Path=Parameter1}" />
<Dgv:DataGridTextColumn Header="Parameter 2"
Binding="{Binding Path=Parameter2}" />
</Dgv:DataGrid.Columns>
</Dgv:DataGrid>
You say you want headers. My recommendation is to create headers with the proper tools, not shoehorn squares into circles because circles exist.
Try this
<Dgv:DataGrid x:Name="dgvRowDetails" ItemsSource="{Binding}">

ComboBox in DataGrid: ItemSource bound to dynamic resource, how to set default row?

I'm working in a C# project [for school], using WPF and implementing MVP. In this code, I've got a DataGrid showing a list of divers. The first column is the Name, and the second column shall show 'DivingType'. DivingType is a built in object, which has a property ID, such as 103A. There are about 400 of these, stored in a list, and each Diver ('row') has a Dives (List<Dive>) Property, and each of these Dives has a divingType property.
What we want to have, is that this column will by default show the DivingType.ID associated with the diver, but that the dropdown list shall contain ALL diving types, such that you shall be able to change it from there [and update the diver object]. To further complicate it, this is one of many views which we add to our window as UserControls.
With that said, here is the code. I've tried to cut out unnecessary clutter which I'm certain has no impact on the result.
<Window ...>
<Window.Resources>
<local:Presenter x:Key="myPresenter"/>
</Window.Resources>
<DockPanel DataContext="{StaticResource myPresenter}">
<UserControl ...>
<DataGrid ItemsSource="{Binding DiverList}" x:Name="datagrid">
<DataGrid.Columns>
<DataGridTextColumn Header="Name" Width="1*" Binding="{Binding Name}"/>
<DataGridTemplateColumn Header="Diving type" Width="1*">
<DataGridTemplateColumn.CellTemplate>
<DataTemplate>
<ComboBox ItemsSource="{Binding RelativeSource={RelativeSource AncestorType=UserControl},
Path=DataContext.DivingTypes}"
DisplayMemberPath="ID"
SelectedValue="{Binding Dives[0]}"
SelectedValuePath="divingType">
</ComboBox>
</DataTemplate>
</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
</DataGrid.Columns>
</DataGrid>
</UserControl>
</DockPanel>
</Window>
When the program runs I get all DivingTypes.ID inside the combobox, but no selected value. The code does not put any related errors into the output window. I believe that what happens is that it calls DivingType.Equals but passing the DataContext for the row (the Diver) instead of the SelectedValuePath which I specify. Any way to override this behaviour inside XAML? Or is there an easier way to achieve this?
EDIT:
I've since edited the code posted above to be:
<ComboBox ItemsSource="{
Binding RelativeSource={
RelativeSource AncestorType=UserControl
},
Path=DataContext.DivingTypes
}"
SelectedValue="{
Binding Dives[0].divingType, Mode=TwoWay
}"
/>
This makes the correct value show in the combobox at the start, DivingType.ID is loaded from the Diver.Dives[0].divingType, but it still does not set the property when I select a new value in the dropdown box.
Use UpdateSourceTrigger=PropertyChanged.
Explanation here.
Have you tried to implemented INotifyPropertyChanged in your viewmodel and then raise the PropertyChanged event when the SelectedValue gets set.
If this is not working, can you set the SelectedValue
SelectedValue="{Binding Path=divingType, Mode=TwoWay}"

TextBlock Text-Binding from ListView items

How can I change the Text of a TextBlock when the selection in my ListView changes?
I don't want do this manually...
All Items of the ListView are LogEntry's (class)... Can I use Binding in the Text-Attribute of the TextBlock to get a specific property of the selected Item?
Yes, in fact there are multiple solutions, i give you the most "WPF" like answer, but imo also the least flexible.
First you need to set the IsSynchronizedWithCurrentItem="True" property
Now if you select an item, the bound CollectionView will set the item as the CurrentItem.
Now your TextBox/Block can bind to this specific item via a special binding syntax using a '/'.
For Example:
<TextBlock Text="{Binding LogEntries/}"/>
of course you can get a specific property from the current item via binding aswell
<TextBlock Text="{Binding LogEntries/WarningMessage}"/>
Hope that helps.
assuming you have a listview like this:
<ListView ItemSource="{Binding LogEntries}" Name="logs" IsSynchronizedWithCurrentItem="True">
</ListView>
<ContentControl Content="{Binding ElementName=logs, Path=SelectedItem}" ContentTemplate="{StaticResource logTemplate}"/>
Now you need to provide that logTemplate in the Resources.
<UserControl.Resources>
<DataTemplate DataType="{x:Type local:LogEntry}">
<TextBlock Text="{Binding Path=LogText}"/> <-- This is a Property-Binding of your custom class
</DataTemplate>
</UserControl.Resources>
The last thing missing is to provide the namespace to your local class LogEntry. If you use an awesome tool like Resharper, it will insert the namespace for you. Otherwise, here a sample declaration:
<UserControl xmlns:local="clr-namespace:My.App.Namespace.LogEntry;assembly=My.App"
... (rest of namespace declarations)

Categories

Resources