How to bind property of the WPF TabItem from MVVM way? - wpf

I want to bind "Header" of the TabItem properties on MVVM way.
I bind the "ItemsSource" property of the "XamTabControl" to a list of view models (List<MyTabItem> MyTabItem is a viewmodel too).
Here is the XAML Code
<igWindows:XamTabControl
Height="198"
HorizontalAlignment="Left"
Margin="0,54,0,0"
ItemsSource="{Binding Tabs}"
Name="xamTabControl1"
VerticalAlignment="Top"
Width="651">
<!-- this is the body of the TabItem template-->
<igWindows:XamTabControl.ItemTemplate>
<DataTemplate>
<TextBlock
Text="{Binding Header}" />
</DataTemplate>
</igWindows:XamTabControl.ItemTemplate>
<igWindows:XamTabControl.ContentTemplate>
<!-- this is the body of the TabItem template-->
<DataTemplate>
<TextBlock
Text="{Binding Content}" />
</DataTemplate>
</igWindows:XamTabControl.ContentTemplate>
</igWindows:XamTabControl>
Here is the view model.
private ObservableCollection<TabItem> tabs;
public ObservableCollection<TabItem> Tabs
{
get
{
return tabs;
}
set
{
tabs = value;
NotifyPropertyChanged("Tabs");
}
}
To display the tab header, I have inserted a text block inside the ItemTemplate in XamlTabControl. I wanna display the header by using the "Header" property of the TabItemEx property instead of using text block.
And I wanna do this to "CloseButtonVisibility" property too.

I got the answer from the stackoverflow. Please look at this post

Related

Modify content of resource

I have a UserControl that contains a TabControl that has an ItemTemplate that in turn has a Button. I want the user be able to change the content of that button, e.g. change it from TextBlock to Image.
A solution I thought of was to set the button's content from the Resources of the UserControl and overwrite the Resource by setting it on the ResourceDictionary of the entailing Window. Of course that does not work as StaticResource always resolves to the "closest" instance it can find.
I then thought of modifying the resource in the constructor of my UserControl, depending on some property. But it seems, one cannot change a resource. Below is a close sample showing the idea with a simple ListBox in a Window in which I try to change "What" to "How".
How would you approach this?
<Window.Resources>
<TextBlock x:Key="key" Text="What: " x:Shared="false" />
</Window.Resources>
<Grid Margin="10">
<ListBox Name="lbTodoList" HorizontalContentAlignment="Stretch">
<ListBox.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<StaticResource ResourceKey="key" />
<TextBlock Text="{Binding}" />
</StackPanel>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
TextBlock tb = FindResource("key") as TextBlock;
tb.Text = "How: ";
List<string> items = new List<string>();
items.Add("Item 1");
items.Add("Item 2");
items.Add("Item 3");
lbTodoList.ItemsSource = items;
}
}
Instead of trying to override a resource, you should just treat it like any other XAML data-binding templating issue.
Instead of:
<StaticResource ResourceKye="key" />
Do something like this:
<TextBlock Text="{Binding LabelText}" />
In your UserControl set the default value of LabelText to "What:" and then allow the user to override the value. The data binding will take care of the rest.
If you want to have more dynamic content, then use a ContentControl instead and have properties for Content, ContentTemplate, and even ContentTemplateSelector depending on what you need to do.
<ContentControl
Content="{Binding MyContent}"
ContentTemplate="{Binding MyContentTemplate}"
ContentTemplateSelector="{Binding MyContentTemplateSelector}" />
This opens up a lot of flexibility.

WPF binding UserControl collection to ItemsControl. DataTemplate issue

http://www.filedropper.com/wpfapplication9
that link has a project in VS2013 with a sample issue.
my problem is, how to set DataTemplate to UserControl, in ItemsControl.
<ItemsControl ItemsSource="{Binding Collection}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
my Collections is a
public IEnumerable<MyUC> Collection {get;}
my MyUC is a
public partial class MyUC : UserControl
{
public string Title { get; set; }
}
When i try to show that collection im getting
MyUC.Content
insted
MyUC.Title
when i change ItemsSouce to ListBox, datatemplate starts working.
but i need to show collection without ListBox addons.
If I understand you correctly, you just want to display the Title property value of your UserControls from your collection. All that you have to do is to declare an appropriate DataTemplate for them in the Resources section:
<DataTemplate DataType="x:Type={YourPrefix:MyUC}">
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
To be honest though, you seem to be going about this in the wrong way. In WPF, we generally don't put UI elements into collections, instead preferring to work with custom data classes that have DataTempates to define what they should look like. In your case, it would look something like this:
<DataTemplate x:Key="TitleTemplate" DataType="x:Type={YourPrefix:MyDataClass}">
<TextBlock Text="{Binding Title}"/>
</DataTemplate>
<DataTemplate x:Key="ControlTemplate" DataType="x:Type={YourPrefix:MyDataClass}">
<YourControlPrefix:MyUC />
</DataTemplate>
You'd then use the TitleTemplate when you want to see just the Title property values and the ControlTemplate when you want to see the whole UserControl.

Drop-down TabControl

I've been trying to create a custom skin/template for a TabControl in WPF.
I want the tabs to be displayed in a ComboBox. When you select the item from the ComboBox, I want the content area of the tab control to display the TabItem contents.
Here's an image showing what I'm looking for:
I could do this using some sort of master-detail setup with data objects and templates, but the problem is I want to set up the controls using the TabControl XAML format, like this:
<TabControl Style="{DynamicResource ComboTabControlStyle}">
<TabItem Header="TabItem1">
<TextBlock Text="TabItem1 Content!" FontSize="18.667" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</TabItem>
<TabItem Header="TabItem2">
<TextBlock Text="TabItem2 Content!" FontSize="18.667" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</TabItem>
<TabItem Header="TabItem3">
<TextBlock Text="TabItem3 Content!" FontSize="18.667" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</TabItem>
</TabControl>
Any thoughts or suggestions?
It is very easy to change the layout of the tab items using a different Panel, but a ComboBox is an ItemsControl, not a Panel.
I tried putting the ComboBox in the TabControl template and binding the ItemsSource of the ComboBox to the TabControl.Items property, but it didn't seem to work correctly.
I also tried creating a custom panel that only shows one "selected" item at a time and shows all of the items in a drop-down when you click on it (basically a "ComboBox" panel). I ran into trouble because visuals can only be in one place in the visual tree. So putting the children of the panel into a popup caused an exception to be thrown.
Anybody have any other ideas?
Thanks for the help!
It's surprisingly difficult to do what you're trying to do. This comes very close:
<DockPanel>
<ComboBox x:Name="ItemSelector" DockPanel.Dock="Top">
<ComboBox.ItemTemplate>
<DataTemplate DataType="{x:Type TabItem}">
<TextBlock Text="{Binding Header}"/>
</DataTemplate>
</ComboBox.ItemTemplate>
<TabItem Header="TabItem1">
<TextBlock Text="TabItem1 Content!" FontSize="18.667" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</TabItem>
<TabItem Header="TabItem2">
<TextBlock Text="TabItem2 Content!" FontSize="18.667" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</TabItem>
<TabItem Header="TabItem3">
<TextBlock Text="TabItem3 Content!" FontSize="18.667" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</TabItem>
</ComboBox>
<ContentPresenter Content="{Binding SelectedItem.Content, ElementName=ItemSelector}" DockPanel.Dock="Top"/>
<TextBlock/>
</DockPanel>
Where it breaks down, oddly, is displaying the selected item in the ComboBox: the ItemTemplate is ignored when rendering the item, so the selection box contains a TabItem. To fix this, you have to subclass ComboBox and implement a read/write SelectionBoxItemTemplate dependency property, because, for some reason that I'm sure is not as stupid as it seems to me at this moment, that property's read-only.
Create a new class, inherit from panel, put a combo box inside, do a lot of parent binding, and use it. it'll do the trick.
You must use custom class to write the tabs as they are regular tab items.
I found a solution.
I created a custom Control class (MasterDetailControl).
This class has two template parts:
[TemplatePart(Name = "PART_MasterSelector", Type = typeof(Selector))]
[TemplatePart(Name = "PART_DetailPresenter", Type = typeof(ContentPresenter))]
The control has an items dependency property:
public IList Items { ... }
I added a helper class:
[ContentProperty("Detail")]
public class MasterDetail
{
public object Master { get; set; }
public object Detail { get; set; }
}
Items that are placed in the Items DP are processed by the MasterDetailControl. If they are of type MasterDetail, the master is added to the selector items list. For other child item types, a new MasterDetail object is created with the object assigned to the master and detail fields. A separate list maintains all of the generated MasterDetail objects with indexes that correspond to those in the Selector control.
When the SelectionChanged event fires on the Selector object, I set the ContentPresenter's Content property to the Detail field of the item corresponding to the selected master object.
If anyone wants more details, feel free to comment.
In the end, I can now use this control with a simple control template specifying any selector object (ListBox, ComboBox, etc) and a ContentPresenter.

Using data binding on value which is a FrameworkElement

One of my data sources produces a collection of values which are typed to the following interface
public interface IData
{
string Name { get; }
FrameworkElement VisualElement { get; }
}
I'd like to use data binding in WPF to display a collection of IData instances in a TabControl where the Name value becomes the header of the tab and the VisualElement value is displayed as the content of the corresponding tab.
Binding the header is straight forward. I'm stuck though on how to define a template which allows me to display the VisualElement value. I've tried a number of solutions with little success. My best attempt is as follows.
<TabControl ItemsSource="{Binding}">
<TabControl.ItemTemplate>
<DataTemplate>
<Label Content="{Binding Name}"/>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
How do I display VisualElement here?
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
I'm still very new to WPF so I could be missing the obvious here.
ContentPresenters were made for this. The content template becomes:
<TabControl.ContentTemplate>
<DataTemplate>
<ContentPresenter Content="{Binding VisualElement}" />
</DataTemplate>
</TabControl.ContentTemplate>
I tested it with a TextBlock and a TextBox.

Silverlight: How to dynamically bind a ComboBox in a ListBox ItemTemplate?

I have a list box that requires at least one ComboBox. I couldn't find a way to place the ComboBox in the ItemTemplate I use.
...
<DataTemplate x:Key="parts_template">
<StackPanel Orientation="Horizontal">
<TextBlock .../>
<ComboBox .../>
</StackPanel>
</DataTemplate>
...
<ListBox x:Name="lb_parts" ItemTemplate="{StaticResource parts_template}" .../>
...
How do bind that ComoBox in the DataTemplate to an ObservableCollection in the code behind?
Another thing you could try is subscribe the Loaded event on the ComboBox.
Then you can set the ComboBox.ItemsSource in the EventHandler to MyObservableCollection.
Have a look
XAML:
<DataTemplate x:Key="parts_template">
<StackPanel Orientation="Horizontal">
<TextBlock .../>
<ComboBox Loaded="ComboBox_OnLoaded">
<!-- ComboBox ItemTemplate -->
</ComboBox>
</StackPanel>
</DataTemplate>
C# Code Behind:
private void ComboBox_OnLoaded(object sender, EventArgs e)
{
((ComboBox)sender).ItemsSource = MyObservableCollection;
}
Okay, here is how you can add a ComboBox to the ListBox in the code behind.
Create a ComboBox
ComboBox x = new ComboBox();
If you have a data source that populates the ComboBox then you can just bind that
x.ItemsSource = e.Result;
If you do not and want to manually add items to the ComboBox:
ComboBoxItem y = new ComboBoxItem();
Set the Content of the Item to what you want displayed in the ComboBox
y.Content = "Hello";
Now all that is left, is to add the ComboBoxItem to the ComboBox (only if you are creating the Items manually), and then the ComboBox to the ListBox
x.Items.Add(y);
//testing is my ListBox
testing.Items.Add(x);
You should be able to set the data context to the List itself
lb_Parts.DataContext=myCollection;
Then you should be able to bind to it in the template
<DataTemplate x:Key="parts_template">
<StackPanel Orientation="Horizontal">
<TextBlock .../>
<ComboBox ItemSource={Binding}/>
</StackPanel>
</DataTemplate>

Resources