How can I get the the selected visual content of a TabControl? - wpf

My custom TabControl uses a ContentTemplate in order to show the selected content. From within the TabControl, how can I get the actual Visual Content? SelectedTabItem.Content is my viewmodel.

The selected content presenter is a templated part of TabControl, so we can do this in our subclass:
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
_selectedContentHost = Template.FindName("PART_SelectedContentHost", this) as ContentPresenter;
}

If you look at the TabControl Class and TabItem Class pages at MSDN, you will see that they both extend the System.Windows.Media.Visual Class. Therefore the whole object is the Visual object and can be used as a parameter in any method that takes a Visual object.

Related

How do I make user controls for both ListView and ListViewItem work with each other?

I have all the styling, triggers, etc. down for ListView and ListViewItem, and I want to turn them into user controls. How do I make sure that these two "match up" with each other, so that MyListView accepts MyListViewItems as content? Also, considering that I must end the ListView tag by the end of the user control XAML file, I am not sure how I would add items to it.
If you want them to be reusable with different data sets, especially through binding, you should stay away from UserControls and just make custom controls derived from the original types. In that case you create a standalone MyListView.cs and MyListViewItem.cs and all of the XAML for the controls goes into default Styles (usually also containing a ControlTemplate) in Themes/Generic.xaml. You can see an example of this setup by just adding a WPF Custom Control to your WPF project from Add New Item.
Once you've created the .cs files for your custom controls you just need to override a few methods from the base ItemsControl to use MyListViewItem as the item container control. The ListView would end up like this:
public class MyListView : ListView
{
static MyListView()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MyListView), new FrameworkPropertyMetadata(typeof(MyListView)));
}
protected override DependencyObject GetContainerForItemOverride()
{
return new MyListViewItem();
}
protected override bool IsItemItsOwnContainerOverride(object item)
{
return item is MyListViewItem;
}
}
You can now use your custom MyListView exactly as you would a normal ListView, including binding to ItemsSource.
Inheritance should take care of that for you. In other words, if you have two user controls, the first one with a basic element of ListView (not UserControl) and the other of ListViewItem (again, not UserControl), and you make sure they extend ListView and ListViewItem respectively in the .cs code, the following should work equally:
ListView lv = new ListView();
lv.Items.Add(new ListViewItem());
or
MyListView mlv = new MyListView();
mlv.Items.Add(new myListViewItem()); //If your myListView extends ListView, and myListViewItem extends ListViewItem in your user control files, of course
In case you are looking for a XAML solution, you should import your namespace at the top
xmlns:myControls="WhateverYourNamespaceAndAssemblyAre"
and on you page/window/whatever
<myControls:myListView>
<myControls:myListViewItem/>
<myControls:myListViewItem/>
</myControls:myListView>

Can I add a DependencyProperty on an windows user control?

I'm trying to host a Visio ActiveX object in a WPF application.
To do this, I created a Windows user control project where I add the Visio object. This windows user control is then hosted on an WPF user control in an WindowsFormsHost object.
<WindowsFormsHost Name="wfHost" Grid.Row="1">
<wf:VisioUserControl FileNamePath="?"/>
</WindowsFormsHost>
What I would like to do is to bind the value of the FileNamePath member to the value of a TextBox element which defines the path.
The project follows the MVVM pattern, so there is no way that I can access the VisioUserControl object in my ViewModel.
The solution I was thinking about is to bind the FileNamePath member to the value of the TextBox that contains the path, but it is not a DependencyProperty and it seems that I'm not able to define one in the code behind of the windows user control.
So, is there any workaround to perform this binding?
Thanks in advance.
You can solve this by creating a UserControl that wraps your VisioUserControl (I wrote a simple tutorial on UserControl creation here). You can then add a FileNamePath dependency property to your UserControl. In the property changed handler of this dependency property, set the FileNamePath property on the VisioUserControl that this user control wraps.
Ok I have created an example of a WPF usercontrol that is hosting a Winforms control, with a dependency property that is bound to the winforms control's text property.
public partial class ActiveXObjectHoster : UserControl
{
private static System.Windows.Forms.Label testObject;
public ActiveXObjectHoster()
{
InitializeComponent();
testObject = new System.Windows.Forms.Label();
windowsFormsHost1.Child = testObject;
}
#region Properties
public static DependencyProperty FileNameProperty = DependencyProperty.Register("FileName", typeof(string), typeof(ActiveXObjectHoster), new UIPropertyMetadata("",new PropertyChangedCallback(OnFileNamePropertyChanged)));
public string FileName
{
get { return (string)GetValue(FileNameProperty); }
set
{
SetValue(FileNameProperty, value);
}
}
private static void OnFileNamePropertyChanged(
DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
testObject.Text = (string)e.NewValue;
}
#endregion
}
Here is the xaml of the control (its very simple)
<UserControl xmlns:my="clr-namespace:System.Windows.Forms.Integration;assembly=WindowsFormsIntegration"
x:Class="WPFTestApp2.Controls.ActiveXObjectHoster"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Name="ObjectHost"
Height="100" Width="100">
<Grid>
<my:WindowsFormsHost x:Name="windowsFormsHost1" />
</Grid>
</UserControl>
What you need to do is change the test object from a Label to whatever Visio object you were using. Then in the property callback change the text property to the filename or whatever property you wanted.
As mentioned above this is done in the code behind, but that is fine for a user control, its completely decoupled from whatever thing is using it, you just need to bind to the filename property of the control.
Here is a link to a project I created showing how the control is used. There is a textbox whos text is bound to the FileName property, which changes the Winforms Labels text.
You can place this in a Winforms Usercontrol if you want to use it in winforms (like you mentioned in your reply to my comment)
Try replacing the label for your control and see if it works.
Why not implement a UserControl to wrap the WindowsFormHost and the Visio user control? Then you cann add a Dependency Property, and implement in the code behind a handler for the PropertyChangedCallback, and appropiately interact with the WinForms control

XAML control to inherit from templates

What controls are there to inherit from that has the property of Children and supports templating when building a custom usercontrol.
Currently I know about Panel, but it does not support properties and methods like DefaultStyleKey and GetTemplateChild();
Is there an interface that I can inherit from for templates such as:
public class Scroller : Panel, ITemplates //Something like ITemplates
{
public override void OnApplyTemplate()
{
base.OnApplyTemplate();
container = GetTemplateChild("container") as StackPanel; //I want to be able to do this
this.Children; //And also be able to use this
}
}
You want ItemsControl. It has an Items property and its template is very flexible as demonstrated by the fact that TabControl, ListBox, ComboBox, etc all derive from it.

Getting WPF Templated items from Control and Data templates

How do I get a named control from a control that is bound to a control template or data template?
I have tried FindName it does not work.
I prefer not to use the VisualTreeHelper as you have to traverse through each parent child item individually.
It depends when you do it. If you do it in the constructor it wont work as the element only exists after the template has been applied.
This is the standard way to do it if you create the control:
public override void OnApplyTemplate() {
//i call the base first
base.OnApplyTemplate();
//then go looking for the newly created elements
TextBox textBox = this.Template.FindName("PART_TextBox", this) as TextBox;
}

How can I determine the type of the items that get added to a TabControl?

I've created a CloseableTabItem control that derives from TabItem.
Now I'd like to specify that a given TabControl should add new items using CloseableTabItem instead of TabItem.
Is this possible? How?
public class CloseableItemsTabControl : TabControl
{
protected override DependencyObject GetContainerForItemOverride()
{
return new CloseableTabItem();
}
}
You'll probably need to make your own ClosableTabControl that extends TabControl in order to override the base functionality.
However, you can also probably just add your tabs manually, feeding it your ClosableTabItems instead of regular TabItems. It would be safe to assume this is possible since most collection-based controls are able to be programatically populated this way.

Resources