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.
Related
The tooltip for the below listbox is set using a setter. Nothing appears for a tooltip on mouse over.
I suspect the issue is the itemssource of the listbox itself. The listbox is bound to a list of AttributeItems called CandidateAttributes. An element of that list is an observablecollection called AttributePath, and the property in the Attribute path I am trying to bind the tooltip to is called ConceptualPath. Below is the definition for CandidateAttributes-
public static List<AttributeItem> CoBRRaAttributes { get; set; }
The AttributeItems class-
public class AttributeItem
{
private string _displayName = "";
private ObservableCollection<CoBRRa_WPF.CoBRRaUtilities.ViewModels.QueryTool.AttributeCollection> _AttributePath;
public AttributeItem(int id, string displayName, ObservableCollection<CoBRRa_WPF.CoBRRaUtilities.ViewModels.QueryTool.AttributeCollection> attributePath)
{
DisplayName = displayName;
AttributePath = attributePath;
}
public ObservableCollection<CoBRRa_WPF.CoBRRaUtilities.ViewModels.QueryTool.AttributeCollection> AttributePath
{
get
{
return _AttributePath;
}
set
{
_AttributePath = value;
}
}
}
The xmal-
<ListBox
Name="lstCandidates"
ItemsSource="{Binding Path=UIProperties.CandidateAttributes}"
>
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=DisplayName}">
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemContainerStyle>
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Control.ToolTip" Value="{Binding AttributePath.ConceptualPath}"/>
</Style>
</ListBox.ItemContainerStyle>
</ListBox>
I can substitute some text in the place of Binding AttributePath.ConceptualPath and the tooltip displays that text. Just can't figure out why it does not work in the binding. How can I get this to work?
You are binding to AttributePath.ConceptualPath but AttributePath returns an ObservableCollection<AttributeCollection> and this one has no ConceptualPath property.
You should either change the type of the AttributePath property to just CoBRRa_WPF.CoBRRaUtilities.ViewModels.QueryTool.AttributeCollection or bind to a specific AttributeCollection, for example the first one:
<Setter Property="Control.ToolTip" Value="{Binding AttributePath[0].ConceptualPath}"/>
Also make sure that ConceptualPath is a public property of the AttributeCollection class.
Edit:
If you want to the display a list of paths in the tooltip, you should use an ItemsControl:
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="Control.ToolTip">
<Setter.Value>
<ItemsControl ItemsSource="{Binding AttributePath}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding ConceptualPath}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Setter.Value>
</Setter>
</Style>
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:
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>
I am trying to create a custom Tag Cloud control. The way it's supposed to work is that the user can give it a collection of strings in the itemsSource and the converted string will be displayed in the UI. The problem is when I try to drop the control into an application I get the following error:"Could not create an instance of type TagCloudControl". Can anyone help?
Code behind
public class TagCloudControl : ListBox
{
static TagCloudControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(TagCloudControl), new FrameworkPropertyMetadata
(typeof(TagCloudControl)));
}
//tags dependency property
public static DependencyProperty TagsProperty = DependencyProperty.Register("Tags",
typeof(IEnumerable<Tag>),
typeof(TagCloudControl));
public CollectionView GroupedTagsView { get; set; }
public TagCloudControl()
{
ItemsSource = Tags;
//group my tags by "name" property
GroupedTagsView = (ListCollectionView)CollectionViewSource.GetDefaultView(Tags);
GroupedTagsView.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
}
public IEnumerable<Tag> Tags
{
get { return (IEnumerable<Tag>)GetValue(TagsProperty); }
set { SetValue(TagsProperty, value); }
}
}
XAML
<Window x:Class="TagCloudDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:src="clr-namespace:TagCloudControlLibrary;assembly=TagCloudControlLibrary"
Title="Window1" Height="300" Width="300">
<Grid>
<src:TagCloudControl HorizontalAlignment="Center" VerticalAlignment="Center" Name="firstTag"/>
</Grid>
</Window>
TagControl xaml
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TagCloudControlLibrary">
<local:CountToBrushConverter x:Key="CountToBrushConverter"/>
<local:CountToFontSizeConverter x:Key="CountToFontSizeConverter"/>
<Style TargetType="{x:Type local:TagCloudControl}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TagCloudControl}">
<Grid>
<WrapPanel Orientation="Horizontal"
Margin="2"
IsItemsHost="True">
</WrapPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<DataTemplate x:Key="TagsTemplate">
<WrapPanel>
<TextBlock Text="{Binding Name}"
FontSize="{Binding ItemCount, Converter={StaticResource CountToBrushConverter}}"
Foreground="{Binding ItemCount, Converter={StaticResource CountToFontSizeConverter}}"/>
</WrapPanel>
</DataTemplate>
</ResourceDictionary>
Tag Class
public class Tag
{
public Tag(string name)
{
Name = name;
}
public string Name { get; set;}
}
Do you need to instantiate Tags?
I'm guessing it's a null reference exception, you set your items source as Tags in your constructor, but you have not instantiated Tags.
public TagCloudControl()
{
Tags = new ObservableCollection<Tags>();
ItemsSource = Tags;
...
}
Edit:
After playing around with code... I'm thinking that Tags might not need to be a DependencyProperty. It appears you want to Bind the ItemsSource to Tags... but you could just make Tags a simple property, and in it, set the ItemsSource to the value passed in:
public TagCloudControl()
{
//this is now empty.
}
public IEnumerable<Tag> Tags
{
get { return (this.ItemsSource as IEnumerable<Tag>); }
set { this.ItemsSource = value; }
}
Also I think your Style might want to be more like this:
<Style TargetType="{x:Type local:TagCloudControl}">
<Setter Property="ItemTemplate">
<Setter.Value>
<DataTemplate DataType="{x:Type local:Tag}">
<TextBlock Text="{Binding Name}"
FontSize="{Binding ItemCount, Converter={StaticResource CountToBrushConverter}}"
Foreground="{Binding ItemCount, Converter={StaticResource CountToFontSizeConverter}}">
</DataTemplate>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:TagCloudControl}">
<Grid>
<WrapPanel Orientation="Horizontal" Margin="2" IsItemsHost="True">
</WrapPanel>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And finally, I think you might be missing ItemCount in your 'Tag' Class.