Easiest way to apply mvvm with NavigationView - wpf

I am still somewhat new to the concept of MVVM and I need to use NavigationView and apply mvvm. Is there any easy way to convert this to navigationview?
I have this code from an mvvm tutorial
<Grid MaxWidth="600" Margin="20 10">
<Grid.Resources>
<DataTemplate DataType="{x:Type vms:MakeReservationViewModel}">
<views:MakeReservationView />
</DataTemplate>
<DataTemplate DataType="{x:Type vms:ReservationListingViewModel}">
<views:ReservationListingView />
</DataTemplate>
</Grid.Resources>
<ContentControl Content="{Binding CurrentViewModel}" />
</Grid>
I understand that I can use NavigationView ItemInvoked method but I need some guidance as to how to call the ViewModel from the ItemInvoked method
private void NavView_ItemInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked)
{
ContentFrame.Navigate(typeof(SettingsPage));
}
else
{
// find NavigationViewItem with Content that equals InvokedItem
var item = sender.MenuItems.OfType<NavigationViewItem>().First(x => (string)x.Content == (string)args.InvokedItem);
NavView_Navigate(item as NavigationViewItem);
}
}
private void NavView_Navigate(NavigationViewItem item)
{
switch (item.Tag)
{
case "MakeReservation":
ContentFrame.Navigate(typeof(MakeReservationPage));
break;
case "ReservationList":
ContentFrame.Navigate(typeof(ReservationListPage));
break;
}}
How do I navigate to the ViewModel?
EDIT
xmlns:ui =“http://schemas.modernwpf.com/2019”
<Grid>
<ui:NavigationView x:Name=“NavView” ItemInvoked=“NavView_ItemInvoked”>
<ui:NavigationView.MenuItems>
<ui:NavigationViewItem Content = “Make Reservation” Tag=“MakeReserve”/>
<ui:NavigationViewItem Content = “Reservation List” Tag=“List”/>
</ui:NavigationView.MenuItems>
<ui:Frame x:Name=“ContentFrame”>
<ui:Frame.ContentTransitions>
<ui:TransitionCollection>
<ui:NavigationThemeTransition/>
</ui:TransitionCollection>
</ui:Frame.ContentTransitions>
</ui:Frame>
</ui:NavigationView>
I am able to change the view without a problem but I wanted to change it so that when I select one, it will navigate to the viewmodel?

Related

How to bind an ItemsControl to a dependency property of a usercontrol?

Trying to understand this better.
I have an ItemsControl defined in my mainview something like this
<ItemsControl Grid.Column="0" Grid.Row="2"
ItemsSource="{Binding Notes}"
ItemTemplate="{Binding Source={StaticResource MyParagraph}}"
>
</ItemsControl>
in which I would like to use a DataTemplate:
<UserControl.Resources>
<DataTemplate x:Key="MyParagraph">
<v:InkRichTextView
RichText="{Binding ?????? "
</DataTemplate>
</UserControl.Resources>
The InkRichTextView is a view with a dependency property, RichText, being used to pass a paragraph from the ObservableCollection(InkRichViewModel) Notes in the mainview to the user control. That is, this works correctly for one paragragh:
<v:InkRichTextView RichText ="{Binding Path=Note}" Grid.Column="0" Grid.Row="0" />
where Note is defined as a paragraph in the MainView.
The problem is, how do I write the DataTemplate and the ItemsControl such that the ItemsControl can pass each paragraph from the observablecollection to the dependency property RichText in the InkRichTextView?
Thanks for any guidance.
(I hope this is understandable!)
Items control:
<ItemsControl x:Name="NotesItemsControl" Grid.Column="2" HorizontalAlignment="Center">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<local:InkRichTextView RichText="{Binding Note}"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Code behind:
class InkRichViewModel : System.ComponentModel.INotifyPropertyChanged
{
#region Note (INotifyPropertyChanged Property)
private string _note;
public string Note
{
get { return _note; }
set
{
if (_note != value)
{
_note = value;
RaisePropertyChanged("Note");
}
}
}
#endregion
public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string p)
{
var propertyChanged = PropertyChanged;
if (propertyChanged != null)
{
propertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(p));
}
}
}
public MainWindow()
{
InitializeComponent();
var item01 = new InkRichViewModel() { Note = "Note 01", };
var item02 = new InkRichViewModel() { Note = "Note 02", };
var item03 = new InkRichViewModel() { Note = "Note 03", };
var item04 = new InkRichViewModel() { Note = "Note 04", };
var item05 = new InkRichViewModel() { Note = "Note 05", };
var itemList = new List<InkRichViewModel>()
{
item01, item02, item03, item04, item05,
};
NotesItemsControl.ItemsSource = itemList;
}
How it looks at runtime:
Is that what you're looking for?
Based on what you describe, it seems that each item in your ItemsControl is a paragraph, the very object you want to assign to the InkRichTextView.RichText property. Is that correct?
If so, keep in mind that within the item template, the data context is the collection item itself - thus, the path you are looking for does not refer to a property of the data context, but to the data context itself.
That is done with the dot (.) path:
<v:InkRichTextView RichText="{Binding .}"/>
I'm posting this as an answer, although the credit goes to O.R.Mapper and Murven for pointing me in the right direction. My post is to help anyone else just learning this.
In very simple terms, the ItemControl performs a looping action over the collection in its ItemsSource. In my case the ItemsSource is a collection of type InkRichViewModel. (Hence the question from Murven). In its looping action, the ItemsSource will create objects from the InkRichViewModel. (Thus, my usercontrol now has an individual datacontext!) Each of these objects will use the ItemTemplate for display. So to simplify things, I moved the DataTemplate from the UserControl Resources to within the ItemControl itself as:
<ItemsControl Grid.Column="0" Grid.Row="2"
ItemsSource="{Binding Notes}"
>
<ItemsControl.ItemTemplate>
<DataTemplate>
<v:InkRichTextView RichText="{Binding Note}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
Now that each of my usercontrols has its own datacontext being assigned by the ItemsControl, the Output window (VS2010) now shows the binding errors. Fixing these errors leads to a working solution.
Hope this helps other newbies like myself. Thanks everyone.
(Ooops! Just saw the answer from Murven but I'll leave this if it helps somebody to understand.)

Is it possible to load and unload usercontrol using triggers only in WPF

I am trying to create MDI kind of functionality whereby I want to load a user control corresponding to the button clicked by user and unload the rest. Every button is associated with a userControl
<Button Content="Worker registration"/> //UserControl1
<Button Content="Worker recognition"/> //UserControl2 ...and so on
<Grid x:Name="UserControlManager"/>
Any reason not to use a tabcontrol? Like this
<TabControl>
<TabItem Header="Control A">
<local:ControlA/>
</TabItem>
<TabItem Header="Control B">
<local:UserControlB/>
</TabItem>
</TabControl>
Or bind all items using the ItemsSource
<TabControl ItemsSource="{Binding MyItems}"/>
There are also third party TabControls that's quite nice, like the one devcomponents provides.
If a TabControl does not suffice (tons of issues I know), you could use a IValueConverter that would convert some property to a view. You could use a Mediator and/or ViewModelLocator, I love MVVM Light from Galasoft. They provide everything through nuget, and even sets up everything for you :)
Add a command for your buttons for selecting the content you want to show. And add the xaml for showing the SelectedControl.
Bad mediator / ViewmodelLocator ;) Use I.E. Galasofts instead like in this post
public class ViewModelLocator : INotifyPropertyChanged
{
private UserControl selectedControl;
private ObservableCollection<UserControl> controls = new ObservableCollection<UserControl>();
public UserControl SelectedControl
{
get { return selectedControl; }
set
{
if (Equals(selectedControl, value)) return;
selectedControl = value;
OnPropertyChanged();
}
}
public ObservableCollection<UserControl> Controls
{
get { return controls; }
set
{
if (Equals(controls, value)) return;
controls = value;
OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Hope it helps!
Cheers
Stian
You can use DataTemplates to load views depending on what data (viweModel) you set
<Window.Resources>
<ResourceDictionary>
<DataTemplate DataType="{x:Type viewModel:ViewModel1}">
<view:View1 />
</DataTemplate>
<DataTemplate DataType="{x:Type viewModel:ViewModel2}">
<view:View2 />
</DataTemplate>
</ResourceDictionary>
</Window.Resources>
Then have a ContentControl where your content will show
<Grid >
<ContentControl Content="{Binding MyContent}" />
</Grid
Use an enumBooleanConverter (How to bind RadioButtons to an enum?) to select a enum with radiobuttons
<RadioButton GroupName="Navigation"
IsChecked="{Binding Path=SelectedNavigationEnum,
Converter={StaticResource enumBooleanConverter},
ConverterParameter={x:Static viewModel:NavigationEnum.EnumValue1},
Mode=TwoWay}">Show View1</RadioButton>
<RadioButton GroupName="Navigation"
IsChecked="{Binding Path=SelectedNavigationEnum,
Converter={StaticResource enumBooleanConverter},
ConverterParameter={x:Static viewModel:NavigationEnum.EnumValue2},
Mode=TwoWay}">Show View2</RadioButton>
When the SelectedNavigationEnum property is changed set the MyContent property to the selected viewModel
public NavigationEnum SelectedNavigationEnum
{
...
set
{
...
Navigate(value);
}
}
protected void Navigate(NavigationEnum part)
{
switch (part)
{
case NavigationEnum.EnumValue1:
ShowView1();
break;
case NavigationEnum.EnumValue2:
ShowView2();
...
}
}
private void ShowView1()
{
ViewModel1 viewModel = ObjectFactory.GetInstance<ViewModel1>();
MyContent = viewModel;
}
When you set MyContent the DataTemplate will load View1 and set the viewModel as its DataContext.

Navigate from MainView to another View

I am new to WPF and i am using WPF Model-View-ViewModel Toolkit 0.1 to get my hands on WPF.
I have a fairly simple question but i cant get my head around it.
How do i display new view from the menu item on the mainview?
This is how my code looks like:
MainView.xaml
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<MenuItem Command="{Binding NewPage}" Header="New Page"
InputGestureText="Ctrl-N" />
</MenuItem>
</Menu>
MainViewModel.cs
private DelegateCommand newPageCommand;
public ICommand NewPage
{
get
{
if (newPageCommand == null)
{
newPageCommand = new DelegateCommand(GoToNewPage);
}
return newPageCommand;
}
}
private void GoToNewPage()
{
???
}
What do i write in the GoToNewPage to display the newPage.xaml?
Usually your application is run entirely in the ViewModels, and Views are used to allow users to interact with the ViewModels in a friendly manner.
In your case, the ViewModel might have a property called CurrentPage, which is bound to a ContentControl.Content in your View. To change pages, the GoToNewPage command would set the CurrentPage property to a NewPageViewModel.
This would cause the ContentControl to realize it's binding has changed, and in the process of updating the binding, it would realize that the Content has changed and it needs to use a new DataTemplate to draw that content.
<ContentControl Content="{Binding CurrentPage}">
<ContentControl.Resources>
<DataTemplate DataType="{x:Type local:HomePageViewModel}">
<local:HomePageView />
</DataTemplate>
<DataTemplate DataType="{x:Type local:NewPageViewModel}">
<local:NewPageView />
</DataTemplate>
</ContentControl.Resources>
</ContentControl>
I have a simple example of this here if you're interested

WPF DataGrid: Blank Row Missing

I am creating a WPF window with a DataGrid, and I want to show the blank "new item" row at the bottom of the grid that allows me to add a new item to the grid. For some reason, the blank row is not shown on the grid on my window. Here is the markup I used to create the DataGrid:
<toolkit:DataGrid x:Name="ProjectTasksDataGrid"
DockPanel.Dock="Top"
Style="{DynamicResource {x:Static res:SharedResources.FsBlueGridKey}}"
AutoGenerateColumns="False"
ItemsSource="{Binding SelectedProject.Tasks}"
RowHeaderWidth="0"
MouseMove="OnStartDrag"
DragEnter="OnCheckDropTarget"
DragOver="OnCheckDropTarget"
DragLeave="OnCheckDropTarget"
Drop="OnDrop"
InitializingNewItem="ProjectTasksDataGrid_InitializingNewItem">
<toolkit:DataGrid.Columns>
<toolkit:DataGridCheckBoxColumn HeaderTemplate="{DynamicResource {x:Static res:SharedResources.CheckmarkHeaderKey}}" Width="25" Binding="{Binding Completed}" IsReadOnly="false"/>
<toolkit:DataGridTextColumn Header="Days" Width="75" Binding="{Binding NumDays}" IsReadOnly="false"/>
<toolkit:DataGridTextColumn Header="Due Date" Width="75" Binding="{Binding DueDate, Converter={StaticResource standardDateConverter}}" IsReadOnly="false"/>
<toolkit:DataGridTextColumn Header="Description" Width="*" Binding="{Binding Description}" IsReadOnly="false"/>
</toolkit:DataGrid.Columns>
</toolkit:DataGrid>
I can't figure out why the blank row isn't showing. I have tried the obvious stuff (IsReadOnly="false", CanUserAddRows="True"), with no luck. Any idea why the blank row is disabled? Thanks for your help.
You must also have to have a default constructor on the type in the collection.
Finally got back to this one. I am not going to change the accepted answer (green checkmark), but here is the cause of the problem:
My View Model wraps domain classes to provide infrastructure needed by WPF. I wrote a CodeProject article on the wrap method I use, which includes a collection class that has two type parameters:
VmCollection<VM, DM>
where DM is a wrapped domain class, and DM is the WPF class that wraps it.
It truns out that, for some weird reason, having the second type parameter in the collection class causes the WPF DataGrid to become uneditable. The fix is to eliminate the second type parameter.
Can't say why this works, only that it does. Hope it helps somebody else down the road.
Vincent Sibal posted an article describing what is required for adding new rows to a DataGrid. There are quite a few possibilities, and most of this depends on the type of collection you're using for SelectedProject.Tasks.
I would recommend making sure that "Tasks" is not a read only collection, and that it supports one of the required interfaces (mentioned in the previous link) to allow new items to be added correctly with DataGrid.
In my opinion this is a bug in the DataGrid. Mike Blandford's link helped me to finally realize what the problem is: The DataGrid does not recognize the type of the rows until it has a real object bound. The edit row does not appear b/c the data grid doesn't know the column types. You would think that binding a strongly typed collection would work, but it does not.
To expand upon Mike Blandford's answer, you must first assign the empty collection and then add and remove a row. For example,
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// data binding
dataGridUsers.ItemsSource = GetMembershipUsers();
EntRefUserDataSet.EntRefUserDataTable dt = (EntRefUserDataSet.EntRefUserDataTable)dataGridUsers.ItemsSource;
// hack to force edit row to appear for empty collections
if (dt.Rows.Count == 0)
{
dt.AddEntRefUserRow("", "", false, false);
dt.Rows[0].Delete();
}
}
Add an empty item to your ItemsSource and then remove it. You may have to set CanUserAddRows back to true after doing this. I read this solution here: (Posts by Jarrey and Rick Roen)
I had this problem when I set the ItemsSource to a DataTable's DefaultView and the view was empty. The columns were defined though so it should have been able to get them. Heh.
This happned to me , i forgot to new up the instance and it was nightmare for me . once i created an instance of the collection in onviewloaded it was solved.
`observablecollection<T> _newvariable = new observablecollection<T>();`
this solved my problem. hope it may help others
For me the best way to implement editable asynchronous DataGrid looks like that:
View Model:
public class UserTextMainViewModel : ViewModelBase
{
private bool _isBusy;
public bool IsBusy
{
get { return _isBusy; }
set
{
this._isBusy = value;
OnPropertyChanged();
}
}
private bool _isSearchActive;
private bool _isLoading;
private string _searchInput;
public string SearchInput
{
get { return _searchInput; }
set
{
_searchInput = value;
OnPropertyChanged();
_isSearchActive = !string.IsNullOrEmpty(value);
ApplySearch();
}
}
private ListCollectionView _translationsView;
public ListCollectionView TranslationsView
{
get
{
if (_translationsView == null)
{
OnRefreshRequired();
}
return _translationsView;
}
set
{
_translationsView = value;
OnPropertyChanged();
}
}
private void ApplySearch()
{
var view = TranslationsView;
if (view == null) return;
if (!_isSearchActive)
{
view.Filter = null;
}
else if (view.Filter == null)
{
view.Filter = FilterUserText;
}
else
{
view.Refresh();
}
}
private bool FilterUserText(object o)
{
if (!_isSearchActive) return true;
var item = (UserTextViewModel)o;
return item.Key.Contains(_searchInput, StringComparison.InvariantCultureIgnoreCase) ||
item.Value.Contains(_searchInput, StringComparison.InvariantCultureIgnoreCase);
}
private ICommand _clearSearchCommand;
public ICommand ClearSearchCommand
{
get
{
return _clearSearchCommand ??
(_clearSearchCommand =
new DelegateCommand((param) =>
{
this.SearchInput = string.Empty;
}, (p) => !string.IsNullOrEmpty(this.SearchInput)));
}
}
private async void OnRefreshRequired()
{
if (_isLoading) return;
_isLoading = true;
IsBusy = true;
try
{
var result = await LoadDefinitions();
TranslationsView = new ListCollectionView(result);
}
catch (Exception ex)
{
//ex.HandleError();//TODO: Needs to create properly error handling
}
_isLoading = false;
IsBusy = false;
}
private async Task<IList> LoadDefinitions()
{
var translatioViewModels = await Task.Run(() => TranslationRepository.Instance.AllTranslationsCache
.Select(model => new UserTextViewModel(model)).ToList());
return translatioViewModels;
}
}
XAML:
<UserControl x:Class="UCM.WFDesigner.Views.UserTextMainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:model="clr-namespace:Cellebrite.Diagnostics.Model.Entities;assembly=Cellebrite.Diagnostics.Model"
xmlns:System="clr-namespace:System;assembly=mscorlib"
xmlns:converters1="clr-namespace:UCM.Infra.Converters;assembly=UCM.Infra"
xmlns:core="clr-namespace:UCM.WFDesigner.Core"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="300">
<DockPanel>
<StackPanel Orientation="Horizontal"
DockPanel.Dock="Top"
HorizontalAlignment="Left">
<DockPanel>
<TextBlock Text="Search:"
DockPanel.Dock="Left"
VerticalAlignment="Center"
FontWeight="Bold"
Margin="0,0,5,0" />
<Button Style="{StaticResource StyleButtonDeleteCommon}"
Height="20"
Width="20"
DockPanel.Dock="Right"
ToolTip="Clear Filter"
Command="{Binding ClearSearchCommand}" />
<TextBox Text="{Binding SearchInput, UpdateSourceTrigger=PropertyChanged}"
Width="500"
VerticalContentAlignment="Center"
Margin="0,0,2,0"
FontSize="13" />
</DockPanel>
</StackPanel>
<Grid>
<DataGrid ItemsSource="{Binding Path=TranslationsView}"
AutoGenerateColumns="False"
SelectionMode="Single"
CanUserAddRows="True">
<DataGrid.Columns>
<!-- your columns definition is here-->
</DataGrid.Columns>
</DataGrid>
<!-- your "busy indicator", that shows to user a message instead of stuck data grid-->
<Border Visibility="{Binding IsBusy,Converter={converters1:BooleanToSomethingConverter TrueValue='Visible', FalseValue='Collapsed'}}"
Background="#50000000">
<TextBlock Foreground="White"
VerticalAlignment="Center"
HorizontalAlignment="Center"
Text="Loading. . ."
FontSize="16" />
</Border>
</Grid>
</DockPanel>
This pattern allows to work with data grid in a quite simple way and code is very simple either.
Do not forget to create default constructor for class that represents your data source.

wpf binding when using datatemplate

ok currently i have this piece of code:
<TabItem Style="{DynamicResource MyStyle" x:Name="TabCustomers" Padding="0,1,4,1"
Header={Binding Path=customersHeader}/>
Now i want to add an icon there so I do (by removing the header above):
<TabItem.Header>
<StackPanel Orientation="Horizontal">
<Image Stretch="UniformToFill" Source="{StaticResource customers}"/>
<TextBlock x:Key="textblock" Margin="4,0,0,0"
Text="{Binding Path=customersHeader}"/>
</StackPanel>
</TabItem.Header>
So far it's ok.
I would like to generalize this using a datatemplate. I assume i have to do this in my resource dictionary:
<DataTemplate x:Key="TabItemCustomersTemplate" DataType="{x:Type TabItem}">
<StackPanel Orientation="Horizontal">
<Image Stretch="UniformToFill" Source="{StaticResource customers}"/>
<TextBlock x:Key="textblock" Margin="4,0,0,0"
Text="{Binding Path=customersHeader}"/>
</StackPanel>
</DataTemplate>
and change this in my tabitem declaration:
<TabItem ... HeaderTemplate="{StaticResource TabItemCustomersTemplate}".../>
So i run into the following issues and questions:
1) binding doesnt work, why?
2) how can i access textblock from c#?
3) how can i generalize this so i dont have to copy this over and over again for different tab items (or other controls for the matter) so that i can pass my own text and image source each time? For example you might use this to create an image button and if you have 20 buttons the code becomes messy.
Any ideas?
Thank you.
if you template the header in a
tabitem, you do not need to set the
data type of the template. the
header is a property of the tab
item, it is actually a property of
type object, you can put anything in
there.
try removing the DataType="{x:Type
TabItem}" and see if it works.
you should not need to access the
textblock from c#, you should make
do with the binding system. place a
custom object in your header. then
bind this object to your textblock
then adjust the object and it will
manipulate the textblock. getting at
an element is always hard if it is
contained in a data template. you
should not need to. if you find
yourself walking the visual tree to
find a visual element you are doing
things the hard way
you can generalise this by following
suggestion 2, using a custom object,
removing the x:Key of your data
template and setting its DataType to
be the type of your custom object.
then wherever your custom object
appears you will get it data
templated properly
Try this, This is working for me
<Window.Resources>
<!-- <BitmapImage x:Key="customers" UriSource="einstein.jpg"/>-->
<DataTemplate x:Key="TabItemCustomersTemplate">
<StackPanel Orientation="Horizontal">
<Image Stretch="UniformToFill" Source="{Binding Path=Customers}"/>
<TextBlock Margin="4,0,0,0" x:Name="txt" Text="{Binding Path=CustomersHeader}"/>
</StackPanel>
</DataTemplate>
</Window.Resources>
<Grid>
<TabControl Name="mytabcontrol">
<TabItem x:Name="TabCustomers" Padding="0,1,4,1" Header="{Binding}" HeaderTemplate="{StaticResource TabItemCustomersTemplate}">
<Label Content="myContent" Background="Red"/>
</TabItem>
</TabControl>
</Grid>
in code behind
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
var lst = new List<People>();
lst.Add(new People() { CustomersHeader = "My Customer" });
this.DataContext = lst;
}
}
public class People
{
public string CustomersHeader { get; set; }
public BitmapImage Customers { get; set; }
}
Further you can find your textblock in code behind using this
TabPanel tabPanel = GetVisualChild<TabPanel>(mytabcontrol);
if (tabPanel != null)
{
foreach (UIElement element in tabPanel.Children)
{
TabItem tabItem = element as TabItem;
var image = FindNameFromHeaderTemplate<TextBlock>(tabItem, "txt");
}
}
public static T FindNameFromHeaderTemplate<T>(TabItem tabItem, String name) where T : UIElement
{
if (tabItem == null)
{
throw new ArgumentNullException("container");
}
if (tabItem.HeaderTemplate == null)
{
return null;
}
ContentPresenter contentPresenter = GetVisualChild<ContentPresenter>(tabItem);
if (contentPresenter == null)
{
return null;
}
T element = tabItem.HeaderTemplate.FindName(name, contentPresenter) as T;
return element;
}
public static T GetVisualChild<T>(Visual referenceVisual) where T : Visual
{
Visual child = null;
for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(referenceVisual); i++)
{
child = VisualTreeHelper.GetChild(referenceVisual, i) as Visual;
if (child != null && child.GetType() == typeof(T))
{
break;
}
else if (child != null)
{
child = GetVisualChild<T>(child);
if (child != null && child.GetType() == typeof(T))
{
break;
}
}
}
return child as T;
}

Resources