This is a migrated thread and some comments may be shown as answers.

Add Button To Tab

10 Answers 302 Views
TabControl
This is a migrated thread and some comments may be shown as answers.
Peter
Top achievements
Rank 2
Peter asked on 08 Jun 2009, 01:02 AM
Hi,
I'm trying to add a close button to my tabs.  My tabs are being created dynamically in code-behind and then added to the TabControl.

 

 

private void AddNewTab(object control, string header)  
{              
    RadTabItem item = new RadTabItem();  
    item.Header = header;                          
    item.Content = control;  
 
    MainTabControl.Items.Add(item);  
    MainTabControl.SelectedItem = item;  


I've gone through the examples I've found on here, but they all use databinding to create the tabs.
Is there a way to add a button to the header using a DataTemplate and still get the header text to display properly?
I've tried setting the RadTabItem's tag to the header and using the following DataTemplate, but I only get the close button to show.
        <DataTemplate x:Key="TabItemHeaderTemplate1">  
            <Grid> 
                <Grid.ColumnDefinitions> 
                    <ColumnDefinition Width="*"/>  
                    <ColumnDefinition Width="Auto"/>  
                </Grid.ColumnDefinitions>                  
                <TextBlock Text="{TemplateBinding Tag}"/>  
                <Button Grid.Column="1" Style="{StaticResource ImageButtonStyle}" business:EventHelper.EnableRoutedClick="True" ToolTipService.ToolTip="Close">  
                    <Button.Content> 
                        <Image Source="Icons/close_32.png" Height="20" Width="20"/>  
                    </Button.Content> 
                </Button> 
            </Grid> 
        </DataTemplate> 
 


Any help would be appreciated.

-Pete

10 Answers, 1 is accepted

Sort by
0
Kiril Stanoev
Telerik team
answered on 10 Jun 2009, 11:05 AM
Hi Peter,
Silverlight does not offer a way to register for an event in the DataTemplate of a control, so we have to come up with workarounds.

On workaround is to add a UserControl in the template that will have a logic to notify when the button is clicked (i.e. the button will be in the UserControl and the UserControl will have access to the button and its properties.

A more general solution will be to use RoutedEvents which will travel down the visual three and can be handled at a root object. The Telerik controls use Routed Events but unfortunately the default Button does not. Our implementation of the routed events is compatible with all controls, so you could attach a routed event to the button and fire it. Then you can handle the routed event and remove the item.

You can find a sample project attached demonstrating this scenario.
Please take a look at it and let us know how it works for you.

Best wishes,
Kiril Stanoev
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
0
Peter
Top achievements
Rank 2
answered on 10 Jun 2009, 02:26 PM
Thanks for the example.

While the code works, it's not quite what I'm looking for.  In your example, you create a few MyViewModel objects and attach them to the tabControls ItemsSource.  I'm creating RadTabItems from button clicks.  I'm looking for a way to use a DataTemplate for the Tab Header to put a button on it, while at the same time manually setting the header text when creating the RadTabItem.  (see my AddNewTab function in my previous post)  I've tried using your example by changing the MyViewModel class and making the Content property a UserControl, but when it's added to the tabControl it does not display properly.  Do you have an example that uses a UserControl for the Content instead of a Textbox in the ContentTemplate?

Thanks
0
Sandi
Top achievements
Rank 1
answered on 10 Jun 2009, 03:43 PM
I would love to see a solution that demonstrates this as well.




0
Accepted
Miroslav
Telerik team
answered on 11 Jun 2009, 11:24 AM
Hello Sandi,

You can define a DataTemplate in the resources (user control or application resources) and then get the DataTemplate from the resource dictionary when needed. I the previous example the header is a string, so we can create DataTemplate with a TextBlock and a button, then when creating the item we set the HeaderTemplate:

var item = new RadTabItem();  
item.Header = "Some String";  
// The template we assign has a button that works with routed events.  
item.HeaderTemplate = this.Resources["TemplateWithCloseButton"as DataTemplate; 

If you are not happy with handling routed events, you can also build up the header element by element. IMO this is harder to maintain, but you can register for the close button Click event in code:

// Alternatively we can add any object to the header, so we can  
// build the header instead of using a template:  
var panel = new StackPanel() { Orientation = Orientation.Horizontal };  
panel.Children.Add(new TextBlock(){ Text = "Some String" });  
var button = new Button() { Content = "x" };  
button.Click += new RoutedEventHandler(OnClose);  
panel.Children.Add(button); 

Hopefully this is what you need,

Kind regards,
Miroslav
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
0
Srinivas
Top achievements
Rank 1
answered on 09 Jul 2009, 08:51 PM
Hi,

I followed your suggestion to create Data Template and attach it to HeaderTemplate of the tab item but in my case the tab item header is set dynamically based on the option it was selected. How can I use binding in the Data Template? For example, in the code behind I am creating the entire tab control and tab items dynamically and when I create the tab item the header is set based on the option the user selected and I cannot hard-code the header in the Data Template. When I attach template it just displays the close button with no tab heading.

Any help?

Thanks
Srinivas
0
Miroslav
Telerik team
answered on 10 Jul 2009, 07:00 AM
Hi Srinivas,

The HeaderTemplate binds to whatever is in the Header of the item (same for Content and ContentTemplate).

This means that if you want to bind a string, you can do something like:

tabItem.Header = "My String"

and then in the template you can have an element that binds to it:

<TextBlock Text="{Binding}" />

This is an empty path binding, i.e. it is bound directly to the header object. If you choose to set a custom object (ViewModel-like) you can bind to one of its properties as well.

Also, if your TabItems are all dynamically created, you may want to consider binding the TabControl to an observable collection and then adding templates (or TemplateSelectors) for the headers and the content of the items. This way you will not have to manage adding/removing and creating TabItems in code.

Hopefully this will help you,

Regards,
Miroslav
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
0
Srinivas
Top achievements
Rank 1
answered on 26 Jul 2009, 10:28 PM
Hello Miroslav,

Do you have any simple sample to demonstrate adding/removing tab items dynamically as you explained below?

Also, if I want to perform certain tasks when we switch tabs, for example if I switch from tab1 to tab2 I would like to perform some cleanup activities in tab1 before switching to tab2. Do you have any sample to demonstrate this?

----------
Also, if your TabItems are all dynamically created, you may want to consider binding the TabControl to an observable collection and then adding templates (or TemplateSelectors) for the headers and the content of the items. This way you will not have to manage adding/removing and creating TabItems in code.
----------

I really appreciate your help on this.

Thanks
Srinivas
0
Miroslav
Telerik team
answered on 30 Jul 2009, 04:27 PM
Hello Srinivas,

I am sorry for the delayed reply.

I am sending you a sample project where the TabControl is bound to a collection of items. The example is using an Observable collection which means that the TabItems will automatically be added/removed as you add/remove items from the collection.

Also the example uses a TemplateSelectors for both the headers and the content of the TabItems. This can be simplified if you use a single template. Then it can be assigned as HeaderTemplate or ContentTemplate, without using selectors.

Providing a collection for the ItemsSource makes more sense when the content of the TabItems is similar, but it can also be used otherwise.

As for doing the cleanup - you can use the SelectionChanged event. There you have the items that have been unselected, you can access them and their content and do the cleanup you need.

Hopefully this will be useful for you,

Best wishes,
Miroslav
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
0
Srinivas
Top achievements
Rank 1
answered on 04 Aug 2009, 07:36 AM
Hello Miroslav,

Thank you very much for your time and exaple projects. As I am very new to Silverlight and no WPF background or any of similar kind, some of the code looks very complex to me to understand. Though I was able to follow most of it but still I am not able to modify the sample code to achieve my desired results. This is purely because I am very new to Silverlight and unable to understand some complex code.

I am looking for a sample in very simple code. For example, please consider below class from your one of the sample code:

public

 

class MyViewModel

 

{

 

public String Title { get; set; }

 

 

public String Content { get; set; }

 

}

In this class the Content is of String type but I would like to change this to an UserControl type so that I can assign new XAML pages as TabItem content. For example if I change the class to:

 

public

 

class MyViewModel

 

 

 

{

 

 

public String Title { get; set; }

 

 

public UserControl  Content { get; set; }

 

}

and then change the assignment to:

 

myViewModelCollection.Add(

new MyViewModel()

 

{

Title = "User Profile"

,

 

Content =

new UserProfilePage();

 

});

 

 

 

where UserProfilePage is a XAML page within the same project. And when the user click on the "Close" button of the TabItem I would like to close the selected TabItem.

I am not sure how to change the XAML page (from the same sample code that you provided) to achieve the result. Can you please suggest me a simple sample code to achieve the result?

All I am trying to do is create TabItems dynamically, following your suggestion that is creating ObservableCollection as ItemSource for the TabControl and place XAML pages as contents for each TabItem (in my case it is based on Menu tree selection) and allow the user to close the tab.

My Menu Tree:
Option 1: UserProfilePage.XAML
Option 2: UserAccountMaintenance.XAML
Option 3: UserPasswordChange.XAML

When Option 1 is selected then I would like to create a NEW TabItem (1st TabItem) and load UserProfilePage.XAML (Title: User Profile) and when Option 2 is selected then I would like to create a NEW TabItem (2nd TabItem) and load UserAccountMaintenance.XAML (Title: Account Maintenance) and when Option 3 is selected then I would like to create another NEW TabItem (3rd TabItem) and load UserPasswordChange.XAML (Title: Password Maintenance). At this point I have 3 TabItems open with 3 different XAML pages loaded. I would like to provide "Close" button or icon for each TabItem so that individual Tabs can be closed at any point.

By default, I would like to create one TabItem and load HomePage.XAML as content and I do not want to provide "Close" button or icon for this default TabItem.

I really appreciate your time and help on this.

Thanks
Srinivas

0
Miroslav
Telerik team
answered on 09 Aug 2009, 11:39 AM
Hi Srinivas,

I am sorry for the delayed reply,

There are a few things that are new as a convept in Silverlight, but I am sure that you will quickly find your way around.

Sometimes in our examples we use the Model-View-ViewModel pattern (you can find a lot of examples and explanation for it online) but if you are just starting with Silverlight you may find it wasier to do more things in C# code, more like a WinForms application.

Generally all ContentControls and ItemsControls accept objects as content, which means that you can put anything as the content of the TabItem (or any ItemsControls).

An easy way to achieve what you need is to handle the SelectionChnaged event of the TreeView and create a new TabItem with the header and content (the UserControl) you need and then add it to the Items collection of the TabControl and then selected (set the SelectedItem property).

The close button could be put as an overlay in the top right corner of the TabControl. Then you will need to handle its clicked event and remove the SelectedItem from the items collection.

My example used an ObservableCollection and binding and you can (and should IMO) use this once you are more comfortable with Silverlight / WPF.

Hopefully you will be able to achieve what you need.

Greetings,
Miroslav
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
Tags
TabControl
Asked by
Peter
Top achievements
Rank 2
Answers by
Kiril Stanoev
Telerik team
Peter
Top achievements
Rank 2
Sandi
Top achievements
Rank 1
Miroslav
Telerik team
Srinivas
Top achievements
Rank 1
Share this question
or