I want to show items in combo box like this.
As shown in this image per item.
Is it possible?
You can answer me in both c# or vb (for WPF)
You can implement this easily by using data binding with item template inside comboBox as following:
First of all you need to create item template for your ComboBox, which can be done in many ways, Iam going to use the simplest way as following:
<ComboBox Width="200" Height="35" VerticalContentAlignment="Center" ItemsSource="{Binding Items}">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type local:ItemViewModel}">
<StackPanel Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="TextBlock">
<Setter Property="FontSize" Value="12"></Setter>
<Setter Property="VerticalAlignment" Value="Center"></Setter>
</Style>
</StackPanel.Resources>
<TextBlock Text="{Binding HintText}"></TextBlock>
<TextBlock>
<Run>( </Run>
<Run FontFamily="{Binding HintFontFamily}" Text="{Binding HintText}"></Run>
<Run> )</Run>
</TextBlock>
</StackPanel>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
Then I am going to create the view Model for ComboBox item and assign the DataContext for the main window to make the binding works correctly
Item view Model:
public class ItemViewModel
{
public string Name { get; set; }
public string HintText { get; set; }
public string HintFontFamily { get; set; }
}
Main window (or your view) code:
public partial class MainWindow : Window
{
public ICollection<ItemViewModel> Items { get; set; }
public MainWindow()
{
Items = new List<ItemViewModel>()
{
new ItemViewModel()
{
Name="First element",
HintText="First font",
HintFontFamily="Lucida Handwriting"
},
new ItemViewModel(){
Name="Second element",
HintText="Second font",
HintFontFamily="Track"
}
};
//set the datacontext which is the binding source
DataContext = this;
}
}
Finally the result you get:
Related
I create dynamically 5 types TabItem for TabControl. For example:
<Style x:Key="StyleCrcs" TargetType="{x:Type TabItem}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="AllowDrop" Value="True"/>
<EventSetter Event="PreviewMouseMove" Handler="TabItem_PreviewMouseMove"/>
<EventSetter Event="Drop" Handler="TabItem_Drop"/>
<Setter Property="ContentTemplate" >
<Setter.Value>
<DataTemplate>
<Grid>
<TextBlock TextWrapping="Wrap" Text="Adres(hex)" FontSize="15" Margin="10,10,396,444"/>
<ComboBox x:Name="AddressCrcs" HorizontalAlignment="Left" Margin="134,11,0,0" VerticalAlignment="Top" Width="120">
<ComboBoxItem>60</ComboBoxItem>
<ComboBoxItem>61</ComboBoxItem>
<ComboBoxItem>62</ComboBoxItem>
<ComboBoxItem>63</ComboBoxItem>
<ComboBoxItem>64</ComboBoxItem>
<ComboBoxItem>65</ComboBoxItem>
<ComboBoxItem>66</ComboBoxItem>
<ComboBoxItem>67</ComboBoxItem>
</ComboBox>
<TextBlock x:Name="Tranz1" TextWrapping="Wrap" Text="Tranzystor 1:" FontSize="15" Margin="10,126,396,337"/>
<TextBlock x:Name="Tranz2" TextWrapping="Wrap" Text="Tranzystor 2:" FontSize="15" Margin="10,156,396,307"/>
</Grid>
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
Each for them have specific constructions.
All of this types inheritance from ICards.
interface ICards
{
string Name { get; set; }
string Address { get; set; }
}
class CardCrcs : ICards
{
public string Name { get; set; }
public string Address { get; set; }
public string Tranzystor1 { get; set; }
public string Tranzystor2 { get; set; }
}
When i create TabItem i add object to list.
Each TabItem has textblocks and I want set text for this textblocks when i change TabItem.
How can i do that?
<TabControl x:Name="TabCards" Grid.Column="1" SelectionChanged="TabCards_SelectionChanged">
var list = new List<ICards>()
var element = (CardCrcs)list[0];
TabCards.SelectedItem.Tranz1.Text = element.Tranzystor1;
TabCards.SelectedItem.Tranz1.Textdont work. How replace this to do that?
EDIT:
When i do that i can take header, but cant take textblock name.
var item = (TabItem)TabCards.SelectedItem;
I suggest binding your TabControl to a list of view model instances for which you also provide DataTemplates (that would map as tab item's ContentTemplate).
See this question and its answer for reference.
You seem to already have different view models defined (e.g. CardCrcs class), so you can define DataTemplate objects with TargetType set for each of them, describing how each tab item should look like.
Inside each of these templates you can use Bindings to properties that are defined by the corresponding classes, e.g. <TextBlock Text="{Binding Address}"/>.
Finally, set ItemsSource of TabControl to a list of view model instances that you can prepared in your DataContext if that is your wish: ItemsSource={Binding}.
This approach would limit a lot the code behind, and would be also easier to understand, IMO.
In my ViewModel I have collection of TabItem.
Each TabItem contains a Name and another ViewModel (inherits from BaseModel with INotifyPropertyChanged). Based on a property of this ViewModel, XAML makes a decission which View should be placed in the ContentTemplate of each tab control item.
I am working with the mvvm pattern and I got this work with switching between tabs. The problem is I get binding exceptions. It is very difficult to explain.
The error message looks like this:
System.Windows.Data Error: 40 : BindingExpression path error: 'ExtraText' property not found on 'object' ''DisplayModel1' (HashCode=8229676)'. BindingExpression:Path=ExtraText; DataItem='DisplayModel1' (HashCode=8229676); target element is 'Label' (Name=''); target property is 'Content' (type 'Object')
To reproduce the error you can use the following code, with sample data provided.
This is the XAML of my starting window:
<Window.Resources>
<DataTemplate x:Key="Model1Template" DataType="{x:Type local:BaseModel}">
<local:DisplayModel1View />
</DataTemplate>
<DataTemplate x:Key="Model2Template" DataType="{x:Type local:BaseModel}">
<local:DisplayModel2View />
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl ItemsSource="{Binding TabItems}">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:TabModel}">
<Label DockPanel.Dock="Left" Content="{Binding Name}"></Label>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type local:BaseModel}">
<ContentControl Content="{Binding ViewModel, Mode=TwoWay}">
<ContentControl.Style>
<Style TargetType="{x:Type ContentControl}">
<Setter Property="ContentTemplate" Value="{StaticResource Model1Template}" />
<Style.Triggers>
<DataTrigger Binding="{Binding Path=ViewModel.ModelType}" Value="Model2">
<Setter Property="ContentTemplate" Value="{StaticResource Model2Template}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
I defined two Templates which are holding the specific View. In the TabControl.ContentTemplate I am checking the enum value of the ViewModel located in my TabModel.
These are the possible Views:
DisplayModel1View
<Grid>
<Label Content="{Binding Name}"></Label>
</Grid>
DisplayModel2View
<StackPanel>
<Label Content="{Binding Name}"></Label>
<Label Content="{Binding ExtraText}"></Label>
</StackPanel>
You can see, the underlying view models have not the same properties. In DisplayModel2View you can see the property ExtraText, mentioned in the error message.
Last but not least my models:
public abstract class BaseModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public abstract ModelType ModelType { get; }
public void OnPropertyChanged([CallerMemberName] string propertyName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
public class DisplayModel1 : BaseModel
{
public string Name { get; set; }
public override ModelType ModelType => ModelType.Model1;
}
public class DisplayModel2 : BaseModel
{
public string Name { get; set; }
public string ExtraText { get; set; }
public override ModelType ModelType => ModelType.Model2;
}
public class TabModel
{
private bool isSelected;
public string Name { get; set; }
public bool IsSelected
{
get { return isSelected; }
set { isSelected = value; }
}
public BaseModel ViewModel { get; set; }
}
//MainViewModel
public class ViewModel
{
public ObservableCollection<TabModel> TabItems { get; set; }
public ViewModel()
{
TabItems = new ObservableCollection<TabModel>();
TabItems.Add(new TabModel()
{
Name = "Tab1",
ViewModel = new DisplayModel1() { Name = "ModelOne" },
IsSelected = true
});
TabItems.Add(new TabModel()
{
Name = "Tab1",
ViewModel = new DisplayModel2() { Name = "ModelTwo", ExtraText = "ExtraTwo" },
IsSelected = false
});
}
}
//Decission, which View should be used
public enum ModelType
{
Model1,
Model2
}
Actually, you don't need that ModelType property. Let WPF find out which view to use for which view-model based on the view-model's type.
Remove the DataTemplates from Window.Resources first. Remove the trigger.
Place your DataTemplates for concrete view-model types (not for ViewModelBase) in your TabControl's resources (this is to make those DataTemplates local for the tab control - we only want them to apply here).
Then, you will get something like:
<TabControl ItemsSource="{Binding TabItems}">
<TabControl.Resources>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}"/>
</Style>
<DataTemplate DataType="{x:Type local:DisplayModel1}">
<local:DisplayModel1View />
</DataTemplate>
<DataTemplate DataType="{x:Type local:DisplayModel2}">
<local:DisplayModel2View />
</DataTemplate>
</TabControl.Resources>
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:TabModel}">
<Label DockPanel.Dock="Left" Content="{Binding Name}"></Label>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type local:BaseModel}">
<ContentControl Content="{Binding ViewModel}"/>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I have a model class ViewItem that has the property AllowDrop. My View Model ViewModel is an observable collection of ViewItem.
The ViewItem property
public bool AllowDrop
{
get
{
return _allowDrop;
}
}
I have a ViewTree thats data source is bound to an instance of the ViewModel, MyItems.
I would like to be able to access bind the ViewTreeItems AllowDrop property to the underlying model, however I can't figure out the correct way to access it.
My XAML for the TreeView looks like this
<TreeView x:Name="ViewsTree"
AllowDragDrop="True"
DragOver="ViewsTree_DragOver"
ItemsSource="{Binding MyItems}"
ItemTemplate="{StaticResource ViewTemplate}"
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="AllowDrop" Value="{Binding}"/>
</Style>
</TreeView>
I am stuck on how to access the ViewItem in the MyView collection to bind to the AllowDrop property.
Here's a bare minimum example.
XAML:
<Grid>
<Grid.Resources>
<ResourceDictionary>
<Style TargetType="{x:Type TreeViewItem}">
<Setter Property="AllowDrop" Value="{Binding AllowDrop}"/>
</Style>
</ResourceDictionary>
</Grid.Resources>
<TreeView ItemsSource="{Binding MyItems}">
<TreeView.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Id}"/>
<TextBlock Margin="5,0" Text="{Binding AllowDrop}"/>
</StackPanel>
</DataTemplate>
</TreeView.ItemTemplate>
</TreeView>
</Grid>
Code behind :
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new ViewModel();
}
}
public class ViewModel
{
public ViewModel()
{
MyItems = new List<ViewItem>();
for (int i = 0; i < 10; i++)
MyItems.Add(new ViewItem { Id = i, AllowDrop = i % 2 == 0 });
}
public List<ViewItem> MyItems { get; set; }
}
public class ViewItem
{
public int Id { get; set; }
public bool AllowDrop { get; set; }
}
Trying to drag something on the TreeListView will work on items with even Id only.
I have XAML related question I have tried to research an answer in vain. I have commented the relevant questions to the XAML. It looks to me this questions is a more complex because of the way I try to arrange things.
Basically I have a main view model used in the TabControl headers and then in the content area I would show a list of items from the main view model. I just don't know how to to the binding. This is the main question. However, I suspect the next and ultimate objectives I have might factor in how to think about this, so I added them too. The rest of the code is for the sake of completeness.
<StackPanel>
<TabControl x:Name="mainsTabControl"
IsSynchronizedWithCurrentItem="True"
IsEnabled="True"
Visibility="Visible"
ItemsSource="{Binding Path=Mains}">
<!-- How to select a different background for the selected header? Note that the background color is "selected tab" if MainContentViewModel.IsActive is not TRUE.
If it is, a different color is chosen. Here this fact is just emulated with IsEnabled property due to well, multi-binding to the rescue (and a converter)? -->
<!-- Is there a clean way to use ICommand binding (RelayCommand) to check if it is OK to change the tab and if necessary, present a dialogue asking for the change? -->
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsEnabled" Value="{Binding IsActive}"/>
</Style>
</TabControl.ItemContainerStyle>
<!-- This binds to every item in the MainViewModel.Mains collection. Note the question about background color. -->
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<TextBlock Text="{Binding Name}" HorizontalAlignment="Center"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<!-- This binding gives reference to the selected MainContentViewModel. -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" Margin="10" DataContext="{Binding ElementName=mainsTabControl, Path=SelectedItem, Mode=OneWay}">
<ItemsControl ItemsSource="{Binding Path=CustomItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical"/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</StackPanel>
using GalaSoft.MvvmLight;
using System.Collections.ObjectModel;
using System.Linq;
namespace WpfDependencyInjection.ViewModel
{
public class MainContentViewModel: ViewModelBase
{
private ObservableCollection<CustomItemViewModel> customItems;
private mainContentDto MainContent { get; set; }
public string Name { get; }
public bool isActive;
public MainContentViewModel(Engine engine, mainContentDto mainContent)
{
MainContent = mainContent;
Name = MainContent.Name;
IsActive = true;
//The custom items belonging to this main content.
var customItems = engine.CustomItemContents.Where(i => i.MainContentId == MainContent.Id).Select(i => new CustomItemViewModel(engine, i));
CustomItems = new ObservableCollection<CustomItemViewModel>(customItems);
}
public ObservableCollection<CustomItemViewModel> CustomItems
{
get
{
return customItems;
}
set
{
customItems = value;
RaisePropertyChanged(nameof(CustomItems));
}
}
public bool IsActive
{
get
{
return isActive;
}
private set
{
isActive = value;
RaisePropertyChanged(nameof(IsActive));
}
}
}
}
public class CustomItemViewModel: ViewModelBase
{
private Engine Engine { get; }
private ItemTypeDto CustomItem { get; set; }
public string Name { get; }
public CustomItemViewModel(Engine engine, ItemTypeDto customItem)
{
Engine = engine;
CustomItem = customItem;
Name = customItem.Name;
}
}
namespace WpfDependencyInjection
{
public class Engine
{
public string Name { get; } = "EngineMan";
public List<mainContentDto> MainContents { get; set; } = new List<mainContentDto>(new[]
{
new mainContentDto { Name = "Main One", Id = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E1"), Version = 1 },
new mainContentDto { Name = "Main Two", Id = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E2"), Version = 1 }
});
public List<ItemTypeDto> CustomItemContents { get; set; } = new List<ItemTypeDto>(new ItemTypeDto[]
{
new ItemType1Dto { MainContentId = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E1"), Name = "ItemType1Dto I", Id = Guid.NewGuid(), Version = 1 },
new ItemType2Dto { MainContentId = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E1"), Name = "ItemType2Dto I", Id = Guid.NewGuid(), Version = 1 },
new ItemType2Dto { MainContentId = Guid.Parse("C51AC758-504B-4914-92DC-5EBE9A1F39E2"), Name = "ItemType2Dto 2", Id = Guid.NewGuid(), Version = 1 }
});
public Engine()
{
}
}
}
<edit: The binding solved partially, though not the ICommand part.
Try this:
<TabControl x:Name="mainsTabControl"
IsEnabled="True"
IsSynchronizedWithCurrentItem="True"
ItemsSource="{Binding Path=Mains}"
SelectedItem="0"
Visibility="Visible">
<TabControl.ItemContainerStyle>
<Style TargetType="{x:Type TabItem}">
<Setter Property="IsSelected" Value="{Binding IsSelected}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter ContentSource="Header" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="HotPink" />
</Trigger>
</Style.Triggers>
</Style>
</TabControl.ItemContainerStyle>
<TabControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:MainContentViewModel}">
<Button Background="{x:Null}"
Command="{Binding SomeCommand}"
Content="{Binding Name}"
FocusVisualStyle="{x:Null}" />
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate DataType="{x:Type local:MainContentViewModel}">
<ItemsControl Margin="10"
VerticalAlignment="Top"
ItemsSource="{Binding Path=CustomItems}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Vertical" />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type local:CustomItem}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
Command:
There may be cleaner ways but here we bind IsSelected for the TabItem to the IsSelected property of the viewmodel. This enables having a command that asks if it is ok to navigate and sets IsSelected to true if it is.
Background:
We also retemplate the tabitem so that background works as we want. If you check with Snoop WPF inserts an extra border when the item is selected.
Side note 1:
Don't put the TabControl into a StackPanel like that. A StackPanel sizes to content and will kill scrolling and draw outside the control. Also it comes with a cost, a deep visual tree is not cheap. Same in the ItemTemplate and the other places. In fact StackPanel is rarely right for anything :)
Side note 2:
If you specify DataType in your DataTemplate you get intellisense and some compiletime checking.
In my project I currently have an ObservableCollection that is populated inside of my ViewModel constructor. This ObservableCollection holds a custom object which has two properties (both strings).
Currently, the XAML/View counterpart holds two separate list boxes, both of which are bonded to a DataTemplate that selects which property to display as an entry in the ListBox. In this case it displays 'propertyOne'.
Is it possible to have a DataTemplate that can select where each ListBox-item goes to depending on the content of 'propertyTwo'?
I have looked into examples similar to my situation, which used CollectionViewSource but I am not too sure as to how I would implement this into my project, as I am fairly new to using WPF and following the MVVM structure. Would this involve creating a filter event in the code-behind the View?
Listed below are snippets of my code that I think would be useful in understanding my question. Any help on solving this would greatly be appreciated.
View
<Window.Resources>
<DataTemplate x:Key="ListBoxTemplate">
<StackPanel>
<TextBlock Text="{Binding Path=propertyOne}" />
</StackPanel>
</DataTemplate>
</Window.Resources>
<ListBox x:Name="ListBoxOne"
Height="Auto"
Width="Auto"
ItemsSource="{Binding TestCollection}"
ItemTemplate="{StaticResource ListBoxTemplate}" />
<ListBox x:Name="ListBoxTwo"
Height="Auto"
Width="Auto"
ItemsSource="{Binding TestCollection}"
ItemTemplate="{StaticResource ListBoxTemplate}" />
ViewModel
public class ViewModel
{
public ObservableCollection<Item> TestCollection { get; set; }
public ViewModel()
{
//populates the collection from an XML file
//with propertyOne & propertyTwo for each item
TestCollection = CustomObjectClass.DeserializeToColl<Item>("path");
}
}
CustomObjectClass
public class CustomObjectClass
{
public string propertyOne { get; set; }
public string propertyTwo { get; set; }
}
<DataTemplate x:Key="ListBoxTemplate">
<StackPanel>
<StackPanel.Style>
<Style>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=propertyTwo}" Value="read">
<Setter Property="StackPanel.Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Text="{Binding Path=propertyOne}" />
</StackPanel>
</DataTemplate>