Listbox items are placed ontop of each other - c#

I can't seem to figure out why the listbox items are place ontop of each other... they should be below one another. Heres some markup and code.
<ListBox x:Name="DeviceList" Background="#ff4c4c4c" BorderThickness="0" ScrollViewer.CanContentScroll="False" MouseEnter="DeviceList_MouseEnter" MouseLeave="DeviceList_MouseLeave"
ManipulationBoundaryFeedback="DeviceList_ManipulationBoundaryFeedback" ItemContainerStyle="{DynamicResource ResourceKey=ListBoxItemStyle}" PreviewMouseDown="DeviceList_PreviewMouseDown"
PreviewMouseMove="DeviceList_PreviewMouseMove" PreviewMouseUp="DeviceList_PreviewMouseUp" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" DockPanel.Dock="Bottom">
<ListBox.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="..\Utilities\Resources\Themes\Slider.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ListBox.Resources>
</ListBox>
#region Constructors
/// <summary>
/// Constructor.
/// </summary>
public ConfiguratorView()
{
InitializeComponent();
foreach (Device device in (Application.Current.Windows[1].DataContext as ConfiguratorViewModel).AvailableDevices)
{
devices.Add(AddDevice(device.Icon + "_def", device.Description));
}
DeviceList.ItemsSource = devices;
}
#endregion
#region Internal Members
/// <summary>
/// Add the device to the list of devices.
/// </summary>
/// <param name="icon"></param>
/// <param name="description"></param>
/// <returns></returns>
private Canvas AddDevice(string icon, string description)
{
Canvas canvas = new Canvas();
canvas.Name = icon;
ContentControl backgroundContent = new ContentControl();
Label label = new Label();
backgroundContent.Template = Application.Current.FindResource(icon) as ControlTemplate;
label.Content = description;
canvas.Children.Add(backgroundContent);
canvas.Children.Add(label);
return canvas;
}
The device list adds the canvas as the item... and then i set the ItemsSource to the List. Loading it shows all icons right on top of the last one. Any thoughts?

Everything will appear on top of each other because Canvas.Top defaults to NaN.
You could manually calculate the appropriate Canvas.Top values, but I would suggest:
Keeping the Device object simple with a property for Description and icon
Creating a DataTemplate for the ListBox items to display those properties.
EDIT:
For example (I haven't tested this)
Say your Device class looks something like this:
public class Device
{
public string Description { get; set; }
public object Icon { get; set; }
}
And then your datatemplete for the ListBox could look like this:
<ListBox ItemsSource="{Binding Devices}">
<ListBox.ItemsTemplate>
<DataTemplate>
<Canvas>
<!-- add items here -->
<TextBlock Text="{Binding Description}" Canvas.Top="5" />
<Canvas>
</DataTemplate>
</ListBox.ItemsTemplate>
</ListBox>

Related

How to set event for every TreeViewItem?

I want to set an event for every TreeViewItem i'm creating dynamically in code. I'm using the following XAML code:
<Window.Resources>
<Style TargetType="TreeViewItem">
<EventSetter Event="Selected" Handler="TreeViewItemSelectionEvent"/>
<EventSetter Event="MouseRightButtonDown" Handler="TreeViewItemRightClickEvent"/>
</Style>
</Window.Resources>
But this only works on a root TreeViewItem in the TreeView. If i click on another item, which is a child of the root, then i allways get back the root.
Can i make this work somehow? I'd love this method, thus you don't need to handle the events in your code which makes it look cleaner.
TL;DR: Use HierarchicalDataTemplate and Styles to display data in a TreeView. Dynamically loading data in your viewmodel will automatically update the TreeView.
Do not manually create TreeViewItems.
In MVVM, what matters the most is the data and the architecture of your data. The general pattern is to define your data and separately define how the view will display your data. Your view is dependent on your data, not the other way around.
So let's create a ProductViewModel which has a ProductName, a list of sub products and a IsSelected property. We equip this class with a method LoadSubProductsCollectionFromDataSource which retrieves data from whatever data source you may have. Here, I just load somme dummy items.
public class ProductViewModel {
/// <summary>
/// Backing field for the IsSelected property
/// </summary>
private bool _isSelected;
/// <summary>
/// Gets or sets the collection of materials used to build this Product.
/// </summary>
public ObservableCollection<ProductViewModel> SubProducts { get; set; } = new ObservableCollection<ProductViewModel>();
/// <summary>
/// Gets or sets the name of this product.
/// </summary>
public string ProductName { get; set; }
/// <summary>
/// Gets or sets the selected state of this product.
/// </summary>
public bool IsSelected {
get => _isSelected;
set {
//The product has been selected or deselected.
if (!_isSelected && SubProducts.Count == 0) {
//We load data into it if not already done.
LoadSubProductsCollectionFromDataSource();
}
_isSelected = value;
}
}
/// <summary>
/// Loads sub products data into this product.
/// </summary>
private void LoadSubProductsCollectionFromDataSource() {
//..
//Do stuff to retrieve your data dynamically and
//add them to the SubProducts collection.
//...
for (int i = 0; i < 5; i++) {
//Add dummy items
SubProducts.Add(new ProductViewModel() { ProductName = "Some product " + i.ToString() });
}
}
}
In your MainWindow.xaml.cs, initialize and expose a collection of view model objects like this:
public partial class MainWindow : Window {
/// <summary>
/// Exposes the root product of the tree
/// </summary>
public ObservableCollection<ProductViewModel> RootProducts { get; } = new ObservableCollection<ProductViewModel>();
public MainWindow() {
InitializeComponent();
RootProducts.Add(new ProductViewModel() { ProductName = "Root product" });
}
}
This collection would normally be stored in a main viewmodel object but for simplicity I just created it in the MainWindow. Notice how I expose it as a property (to allow Binding) and as an ObservableCollection (to automatically notify the view when the collection changes).
Finally, tell your view how to display your ProductViewModel objects using a TreeView:
<Window x:Class="WpfApp1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfApp1"
mc:Ignorable="d"
x:Name="window"
Title="MainWindow" Height="450" Width="800">
<Window.Resources>
<!--Tell the treeview how to hierarchically display and organize ProductViewModel items-->
<HierarchicalDataTemplate DataType="{x:Type local:ProductViewModel}" ItemsSource="{Binding SubProducts}">
<TextBlock Text="{Binding ProductName}"></TextBlock>
</HierarchicalDataTemplate>
<!--Tell each treeviewitem to bind IsSelected to the PoductViewModel.ISSelected property.-->
<Style TargetType="TreeViewItem">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"></Setter>
</Style>
</Window.Resources>
<Grid>
<TreeView ItemsSource="{Binding ElementName=window, Path=RootProducts}"/>
</Grid>
</Window>
Now, every time you select a TreeViewItem in your TreeView, its IsSelected property is set to true (this is the behavior of a TreeViewItem). Because of our binding, it also sets to true the IsSelected property of the corresponding ProductViewModel. In the setter of this property, we make a call to populate the list of subproducts. Because this list is actually an ObservableCollection, it notifies the View (the TreeView) which knows it should update itself with new TreeViewItems.

How to add images from Resources.resx in a combobox

I have searched a lot but cannot get what I want. I need to fill a combo box with images (114 images embedded in Resources.resx).
I am just getting list, not images. Here is my code.
ResourceSet rsrcSet =MyProject.Properties.Resources.ResourceManager.GetResourceSet(CultureInfo.CurrentCulture, true, true);
List<object> images = new List<object>();
foreach (DictionaryEntry entry in rsrcSet)
{
//String name = entry.Key.ToString();
//Object resource = entry.Value;
images.Add( Don't know what will be here? );
}
var comboBox = sender as ComboBox;
comboBox.ItemsSource = images;
and my XAML
<ComboBox HorizontalAlignment="Left" Grid.Column="0" Grid.Row="0" VerticalAlignment="Top" Width="320" Loaded="ComboBox_Loaded" SelectionChanged="ComboBox_SelectionChanged"/>
It's the easiest to use an item template. To do so we define a DataTemplate with DataType String and set it to the ComboBox.ItemTemplate. In order to use String in XAML we need to reference the xmlns:system="clr-namespace:System;assembly=mscorlib" assembly and namespace. For the binding we use a ObservableCollection<string> that holds the relative paths to your images:
View model:
public class ViewModel : INotifyPropertyChanged
{
public TestViewModel()
{
this.ImageSources = new ObservableCollection<string>() { #"Resources\Icons\image.png" };
}
/// <summary>
/// Event fired whenever a child property changes its value.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Method called to fire a <see cref="PropertyChanged"/> event.
/// </summary>
/// <param name="propertyName"> The property name. </param>
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private ObservableCollection<string> imageSources;
public ObservableCollection<string> ImageSources
{
get => this.imageSources;
set
{
this.imageSources = value;
OnPropertyChanged();
}
}
}
Xaml:
<Window x:Class="MainWindow"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<Window.DataContext>
<viewModels:ViewModel />
</Window.DataContext>
<Window.Resources>
<DataTemplate x:Key="ComboBoxItemTemplate" DataType="system:String">
<Image Source="{Binding}" Height="100" Width="100"/>
</DataTemplate>
</Window.Resources>
<Grid>
<StackPanel>
<ComboBox ItemTemplate="{StaticResource ComboBoxItemTemplate}"
ItemsSource="{Binding ImageSources}" />
</StackPanel>
</Grid>
</Window>
To make it work, your dictionary should contain the relative image paths. If not you have to convert. So instead of initializing the ObservableCollection in the constructor, like in the example, you can move the initialization to anywhere else.

Set itemtemplate for a listbox inside usercontrol

I have a Listbox inside a UserControl to manage different entities with different properties. The UserControl is the same for all entities.
I use MVVM and i bind a generic ViewModel as DataContext for the UserControl.
I wish to set the ItemTemplate for the Listbox in the container xaml for the UserControl in order to display the entity properties.
For the entity "Emlployee" I need to display FullName, for the entity Certifying I need to display CertifyingAuthor and CertifyingDate and so on.
What I need is something similar to that
<StackPanel Grid.Row="0" Orientation="Vertical">
<uc:SearchableFromListTextBox ItemTemplateForTheInsideListBox="{StaticResource Something}" ></uc:SearchableFromListTextBox>
Should I add a dependencyProperty ItemTemplateForTheInsideListBoxProperty to the UserControl? And how i could pass it as itemtemplate of the Listbox?
Hope the question is well explained considering my italian native language.
thanks
EDIT : I give up. This is a control for keyboard data entry and something similar to autocomplete.
Since i am forced to agree to a compromise with MVVM :( i will choose some dirty way to resolve.
Thanks to all
A DataTemplateSelector can do what I think you want. You can define different templates from your user control's XAML, then have the selector choose among them.
Assume these model classes:
public class Employee
{
public Employee(string fullName)
{
FullName = fullName;
}
public string FullName { get; }
}
public class Certifying
{
public Certifying(string certifyingAuthor, DateTime certifyingDate)
{
CertifyingAuthor = certifyingAuthor;
CertifyingDate = certifyingDate;
}
public string CertifyingAuthor { get; }
public DateTime CertifyingDate { get; }
}
Your user control's XAML has a ListBox, which in turn uses a template selector (that we'll get to in a moment) -- and also defines different templates for the two different model types:
<UserControl
x:Class="WPF.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPF"
>
<Grid>
<ListBox x:Name="listBox">
<ListBox.ItemTemplateSelector>
<local:MyTemplateSelector>
<local:MyTemplateSelector.EmployeeTemplate>
<DataTemplate DataType="local:Employee">
<TextBlock
Foreground="Red"
Text="{Binding FullName}"
/>
</DataTemplate>
</local:MyTemplateSelector.EmployeeTemplate>
<local:MyTemplateSelector.CertifyingTemplate>
<DataTemplate DataType="local:Certifying">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="120" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Foreground="Blue"
Text="{Binding CertifyingAuthor}"
/>
<TextBlock
Grid.Column="1"
Foreground="Green"
Text="{Binding CertifyingDate}"
/>
</Grid>
</DataTemplate>
</local:MyTemplateSelector.CertifyingTemplate>
</local:MyTemplateSelector>
</ListBox.ItemTemplateSelector>
</ListBox>
</Grid>
</UserControl>
The user control's code-behind, where I just assign a list of model objects to the list box (for simplicity):
public partial class UserControl1
{
public UserControl1()
{
InitializeComponent();
listBox.ItemsSource = new List<object>
{
new Employee("Donald Duck"),
new Certifying("Mickey Mouse", DateTime.Now),
new Employee("Napoleon Bonaparte"),
new Certifying("Homer Simpson", DateTime.Now - TimeSpan.FromDays(2)),
};
}
}
And finally, the template selector. Its two properties were set from the user control's XAML; the logic of SelectTemplate decides which one to apply:
public class MyTemplateSelector : DataTemplateSelector
{
/// <summary>
/// This property is set from XAML.
/// </summary>
public DataTemplate EmployeeTemplate { get; set; }
/// <summary>
/// This property is set from XAML.
/// </summary>
public DataTemplate CertifyingTemplate { get; set; }
public override DataTemplate SelectTemplate(object item, DependencyObject container)
{
if (item is Employee)
{
return EmployeeTemplate;
}
if (item is Certifying)
{
return CertifyingTemplate;
}
return base.SelectTemplate(item, container);
}
}
And, for completeness, usage of the user control:
<Grid>
<wpf:UserControl1 />
</Grid>
The end result, where Napoleon is currently selected, and Homer suffers a mouse-over:

WPF ComboBox selection change on TabItem selection change

I have a combobox in a tab item in MVVM. This tab can be created multiple times in my application (same view, same view model but different instance), so I can switch from one tab to another (but they are tab of the same type).
It works perfectly with every WPF control, but with combobox I have a strange behaviour:
the focus tab, when it loses focus, gets the selected item of the combox box of the tab that the application is focusing on.
If I switch from 2 tabs that are not of the same type everything works correctly, any idea about that? Thanks.
XAML:
<CollectionViewSource x:Key="StatusView" Source="{Binding Path=StatusList}"/>
<ComboBox Name="_spl2Status" Grid.Column="3" Grid.Row="0"
ItemsSource="{Binding Source={StaticResource StatusView}}"
SelectedValue="{Binding Path=CurrentSPL2.ID_SPL2_STATUS, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
SelectedValuePath="FL_TYPE"
DisplayMemberPath="ID_TYPE">
</ComboBox>
VM:
public List<NullableByteEnumType> StatusList
{
get
{
return (SPC_SPL2.SPL2StatusCollection.Skip(1)).ToList();
}
}
private SPC_SPL2 _currentSPL2 = null;
public SPC_SPL2 CurrentSPL2
{
get
{
if (_currentSPL2== null)
Controller.Execute(delegate(IResult result)
{
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("FL_ACTIVE", true);
parameters.Add("ID_SPL2", _itemcode);
Model.Invalidate(typeof(SPC_SPL2), Filter.GENERIC<SPC_SPL2>(parameters, "ID_SPL2"));
Model.Include<SPC_SPL2>();
if (Model.Appendload(result) == false)
return false;
Debug.Assert(Context.SPC_SPL2.Count == 1);
_currentSPL2= Context.SPC_SPL2.FirstOrDefault();
return result.Successful;
});
return _currentSPL2;
}
set
{
_currentSPL2= value;
OnPropertyChanged(() => CurrentSPL2);
}
}
my tabs are handled in this way:
<Grid>
<Border Grid.Row="0">
<ContentControl
Content="{Binding Path=Workspaces}"
ContentTemplate="{StaticResource MasterWorkspacesTemplate}"
/>
</Border>
</Grid>
where
<DataTemplate x:Key="MasterWorkspacesTemplate">
<TabControl IsSynchronizedWithCurrentItem="True"
BorderThickness="0"
ItemsSource="{Binding}"
SelectedItem="{Binding}"
ItemContainerStyleSelector="{StaticResource TabItemTemplate}"
/>
</DataTemplate>
and workspaces (my viewmodels list) (T is a class who inherit from viewModelBase)
public T CurrentWorkspace
{
get { return WorkspacesView.CurrentItem as T; }
}
private ObservableCollection<T> _workspaces;
public ObservableCollection<T> Workspaces
{
get
{
if (_workspaces == null)
{
_workspaces = new ObservableCollection<T>();
_workspaces.CollectionChanged += _OnWorkspacesChanged;
}
return _workspaces;
}
}
protected ICollectionView WorkspacesView
{
get
{
ICollectionView collectionView = CollectionViewSource.GetDefaultView(Workspaces);
Debug.Assert(collectionView != null);
return collectionView;
}
}
I have recreated your problem. But I couldn't find any issue. Please look at the code below and you might get the solustion. Here is my solution.
MyTab view model
public class MyTab : ViewModelBase
{
#region Declarations
private ObservableCollection<string> statusList;
private string selectedStatus;
#endregion
#region Properties
/// <summary>
/// Gets or sets the header.
/// </summary>
/// <value>The header.</value>
public string Header { get; set; }
/// <summary>
/// Gets or sets the content.
/// </summary>
/// <value>The content.</value>
public string Content { get; set; }
/// <summary>
/// Gets or sets the status list.
/// </summary>
/// <value>The status list.</value>
public ObservableCollection<string> StatusList
{
get
{
return statusList;
}
set
{
statusList = value;
NotifyPropertyChanged("StatusList");
}
}
/// <summary>
/// Gets or sets the selected status.
/// </summary>
/// <value>The selected status.</value>
public string SelectedStatus
{
get
{
return selectedStatus;
}
set
{
selectedStatus = value;
NotifyPropertyChanged("SelectedStatus");
}
}
#endregion
}
MainViewModel view model
public class MainViewModel : ViewModelBase
{
#region Declarations
private ObservableCollection<MyTab> tabs;
private MyTab selectedTab;
#endregion
#region Properties
/// <summary>
/// Gets or sets the tabs.
/// </summary>
/// <value>The tabs.</value>
public ObservableCollection<MyTab> Tabs
{
get
{
return tabs;
}
set
{
tabs = value;
NotifyPropertyChanged("Tabs");
}
}
/// <summary>
/// Gets or sets the selected tab.
/// </summary>
/// <value>The selected tab.</value>
public MyTab SelectedTab
{
get
{
return selectedTab;
}
set
{
selectedTab = value;
NotifyPropertyChanged("SelectedTab");
}
}
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="MainViewModel"/> class.
/// </summary>
public MainViewModel()
{
this.Tabs = new ObservableCollection<MyTab>();
MyTab tab1 = new MyTab();
tab1.Header = "tab1";
tab1.Content = "Tab 1 content";
ObservableCollection<string> tab1StatusList = new ObservableCollection<string>();
tab1StatusList.Add("tab1 item1");
tab1StatusList.Add("tab1 item2");
tab1StatusList.Add("tab1 item3");
tab1.StatusList = tab1StatusList;
tab1.SelectedStatus = tab1StatusList.First();
this.Tabs.Add(tab1);
MyTab tab2 = new MyTab();
tab2.Header = "tab2";
tab2.Content = "Tab 2 content";
ObservableCollection<string> tab2StatusList = new ObservableCollection<string>();
tab2StatusList.Add("tab2 item1");
tab2StatusList.Add("tab2 item2");
tab2StatusList.Add("tab2 item3");
tab2.StatusList = tab2StatusList;
tab2.SelectedStatus = tab2StatusList.First();
this.Tabs.Add(tab2);
this.SelectedTab = tab1;
}
#endregion
}
And finally this is my XAML
<Window x:Class="ComboboxSelectedItem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:ComboboxSelectedItem.ViewModels"
Title="MainWindow" Height="350" Width="525">
<Grid Name="mainGrid">
<Grid.DataContext>
<viewModel:MainViewModel />
</Grid.DataContext>
<TabControl
ItemsSource="{Binding Tabs, Mode=TwoWay}"
SelectedItem="{Binding SelectedTab}">
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Header}" Margin="0 0 20 0"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<!--Content section-->
<TabControl.ContentTemplate>
<DataTemplate>
<Grid>
<StackPanel Orientation="Vertical">
<TextBlock
Text="{Binding Content}" />
<ComboBox
ItemsSource="{Binding StatusList}"
SelectedItem="{Binding SelectedStatus}" />
</StackPanel>
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</Window>
Are you absolutely sure that you are creating a new instance of the viewmodel. If not, then the comboboxes are sharing the same collectionviewsource which means that a change in one combobox will be reflected in all comboboxes. I had this same problem myself.
Try declaring the collection view source in code:
CollectionViewSource StatusListViewSource = new CollectionViewSource();
StatusListViewSource.Source = SPL2StatusCollection;
then in xaml change binding to the collectionviewsource:
ItemsSource="{Binding StatusListViewSource.View}"
I converted from vb so it might need some edits.
Does that help?

Binding text file to a XAML ListBox control

How do I bind a Text File name, to a ListBox control in a Windows Metro style application using C#?
Or:
How do I bind a Dictionary() to a XAML ListBox control?
After what seemed to be endless hours of reading through documentation, searching, and asking questions; I managed to figure this out for myself.
Below is the code that works just as I need it to in my situation:
XAML:
<ListBox Name="NotesList">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Filename}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
Code-behind:
public class NoteView
{
public string Filename { get; set; }
public string Contents { get; set; }
}
/// <summary>
/// Invoked when this page is about to be displayed in a Frame.
/// </summary>
/// <param name="e">Event data that describes how this page was reached. The Parameter
/// property is typically used to configure the page.</param>
protected override void OnNavigatedTo(NavigationEventArgs e)
{
var noteList = new ObservableCollection<NoteView>();
for (int i = 0; i < 10; i++)
{
noteList.Add(new NoteView { Filename = "Sample note " + i });
}
NotesList.ItemsSource = noteList;
}

Categories

Resources