[WPF] Получение уже примененного DataTemplate у TabControl
От: lsoft  
Дата: 20.02.09 11:00
Оценка:
Добрый день.
Подскажите, пожалуйста, такую вещь:
WPF окно, есть TabControl, в Items этого табконтрола есть два разных объекта, Class1 И Class2. В Ресурсах окна есть два <DataTemplate DataType="{x:Type src:Class1}"> и <DataTemplate DataType="{x:Type src:Class2}">

Таким образом, TabControl сам выбирает подходящий дататемплейт из этих двух.

Проблема. При смене таба в SelectionChanged не получается пощупать примененный к SelectedItem ContentTemplate (выбранный табом из двух DataTemplate).
Аналогично пробовал получить TabItem и из него контент темплейт, но везде null. Пробовал и с ContentTemplateSelector, но там тоже свои причуды.

Хочется получить этот темплейт ПЕРЕД показом другой вкладки, чтобы провести в нем некоторые изменения на лету.

Пожалуйста, посмотрите проект
http://rapidshare.com/files/200315961/WpfApplication4.zip.html
MD5: DC14AC4BD0C09413BA335D99F85DF0D5
Re: [WPF] Получение уже примененного DataTemplate у TabContr
От: MxKazan Португалия  
Дата: 20.02.09 11:12
Оценка:
Здравствуйте, lsoft, Вы писали:

L>Проблема. При смене таба в SelectionChanged не получается пощупать примененный к SelectedItem ContentTemplate (выбранный табом из двух DataTemplate).

L>Аналогично пробовал получить TabItem и из него контент темплейт, но везде null. Пробовал и с ContentTemplateSelector, но там тоже свои причуды.
Можно попробовать достучаться до темплейтов, используя DataTemplateKey.
DataTemplateKey key = new DataTemplateKey(typeof(Class1));
DataTemplate template = this.FindResource(key) as DataTemplate;

Ищи прямо в ресурсах TabControl'а, они должны видеть все вышележащие.
Re[2]: [WPF] Получение уже примененного DataTemplate у TabCo
От: Аноним  
Дата: 20.02.09 11:18
Оценка:
MxKazan,
спасибо за ответ, но в проекте почти именно так и написано, однако попытка поиска элемента в шаблоне приводит к
"Данная операция допустима только для элементов, к которым применяется этот шаблон."


            DataTemplateKey key = new DataTemplateKey(this.tabControl.SelectedItem.GetType());
            DataTemplate fooTemplate = this.tabControl.FindResource(key) as DataTemplate;
            var contentPresenter = this.tabControl.Template.FindName("PART_SelectedContentHost", this.tabControl) as ContentPresenter; ;
            
            var _def1 = fooTemplate.FindName("def1", contentPresenter);
            var _def2 = fooTemplate.FindName("def2", contentPresenter);
Re[3]: [WPF] Получение уже примененного DataTemplate у TabCo
От: MxKazan Португалия  
Дата: 20.02.09 11:36
Оценка:
Здравствуйте, Аноним, Вы писали:

А>спасибо за ответ, но в проекте почти именно так и написано, однако попытка поиска элемента в шаблоне приводит к

А>"Данная операция допустима только для элементов, к которым применяется этот шаблон."
Что нужно сделать в шаблоне? Может лучше это через какие-нибудь биндинги реализовать?
Re[4]: [WPF] Получение уже примененного DataTemplate у TabCo
От: lsoft  
Дата: 20.02.09 11:54
Оценка:
Здравствуйте, MxKazan, Вы писали:

MK>Здравствуйте, Аноним, Вы писали:


А>>спасибо за ответ, но в проекте почти именно так и написано, однако попытка поиска элемента в шаблоне приводит к

А>>"Данная операция допустима только для элементов, к которым применяется этот шаблон."
MK>Что нужно сделать в шаблоне? Может лучше это через какие-нибудь биндинги реализовать?

нужно найти в шаблоне кнопку и модифицировать у нее контекстное меню. на разных вкладках (одного типа) будут разные пункты в контекстном меню. точнее даже не разные, а произвольные.
Re[5]: [WPF] Получение уже примененного DataTemplate у TabCo
От: MxKazan Португалия  
Дата: 20.02.09 12:21
Оценка:
Здравствуйте, lsoft, Вы писали:

L>нужно найти в шаблоне кнопку и модифицировать у нее контекстное меню. на разных вкладках (одного типа) будут разные пункты в контекстном меню. точнее даже не разные, а произвольные.

Повесить обработчик на раскрытие контекстное меню и уже в зависимости от кого раскрывается, добавлять нужные menu item'ы.
Re[5]: [WPF] Получение уже примененного DataTemplate у TabCo
От: lsoft  
Дата: 20.02.09 12:32
Оценка:
Получилось сделать что-то похожее. Если в TabControl.Items добавлять объекты своих классов (Class1,Class2) и предоставить TabControl'у возможность самому искать подходящий шаблон из ресурсов, то таб контрол начинает отображать одинаковое содержимое как в заголовке вкладки, так и в содержимом вкладки.
Мне удалось находить шаблон в ЗАГОЛОВКЕ вкладки и менять его. Привожу код.
Осталось только научиться находить объект шаблона для СОДЕРЖИМОГО вкладки.


    <Window.Resources>
    <DataTemplate  DataType="{x:Type src:Class1}">
        <Border BorderBrush="Red" BorderThickness="1">
            <StackPanel>
                <Label Name="def1" Content="default1"/>
                <Label Content="{Binding Path=a1}"/>
            </StackPanel>
        </Border>
    </DataTemplate>

    <DataTemplate DataType="{x:Type src:Class2}">
        <Border BorderBrush="Green" BorderThickness="1">
            <StackPanel>
                <Label Name="def2" Content="default2"/>
                <Label Content="{Binding Path=a2}"/>
            </StackPanel>
        </Border>
    </DataTemplate>

    </Window.Resources>



        <TabControl
            Name="tabControl"
            Loaded="tabControl_Loaded"
            SelectionChanged="tabControl_SelectionChanged"

            >

        </TabControl>


в конструкторе окна добавляем


            this.tabControl.Items.Add(new Class1("-"));
            this.tabControl.Items.Add(new Class2());
            this.tabControl.Items.Add(new Class1("+"));



в tabControl_Loaded пишем


            var c0 = this.tabControl.ItemContainerGenerator.ContainerFromIndex(0) as TabItem;
            c0.GotFocus += new RoutedEventHandler(c0_GotFocus);

            var c1 = this.tabControl.ItemContainerGenerator.ContainerFromIndex(1) as TabItem;
            c1.GotFocus += new RoutedEventHandler(c0_GotFocus);

            var c2 = this.tabControl.ItemContainerGenerator.ContainerFromIndex(2) as TabItem;
            c2.GotFocus += new RoutedEventHandler(c0_GotFocus);

            tabLoaded = true;


tabLoaded — объявлена в классе окна, основная функция, где все делается:

void c0_GotFocus(object sender, RoutedEventArgs e)
        {
            DataTemplateKey key = new DataTemplateKey((sender as TabItem).Content.GetType());

            DataTemplate fooTemplate = base.FindResource(key) as DataTemplate;

            ContentControl contentCtrl = (sender as TabItem) as ContentControl;

            var contentPresenter = FindVisualChild<ContentPresenter>(
                //this.tabControl);
                (sender as TabItem));

            //DataTemplateKey key = new DataTemplateKey((sender as TabItem).Content.GetType());
            //DataTemplate fooTemplate = this.tabControl.FindResource(key) as DataTemplate;

            var _def1 = fooTemplate.FindName("def1", contentPresenter);
            var _def2 = fooTemplate.FindName("def2", contentPresenter);

            if (_def1 != null)
                (_def1 as Label).Content = new Random().Next(); 
            
            if (_def2 != null)
                (_def2 as Label).Content = new Random().Next();
        }


вспомогательная функция


private childItem FindVisualChild<childItem>(DependencyObject obj)
            where childItem : DependencyObject
        {
            for (int i = 0; i < VisualTreeHelper.GetChildrenCount(obj); i++)
            {
                DependencyObject child = VisualTreeHelper.GetChild(obj, i);
                if (child != null && child is childItem)
                    return (childItem)child;
                else
                {
                    childItem childOfChild = FindVisualChild<childItem>(child);
                    if (childOfChild != null)
                        return childOfChild;
                }
            }
            return null;
        }



Надеюсь, кто-нибудь сделает следущий шаг и научится брать шаблон содержимого вкладки. Я чувствую, что я уже почти пас. Второй день насилую себе мозг.
Re[6]: [WPF] Получение уже примененного DataTemplate у TabCo
От: lsoft  
Дата: 20.02.09 12:33
Оценка:
Здравствуйте, MxKazan, Вы писали:

MK>Здравствуйте, lsoft, Вы писали:


L>>нужно найти в шаблоне кнопку и модифицировать у нее контекстное меню. на разных вкладках (одного типа) будут разные пункты в контекстном меню. точнее даже не разные, а произвольные.

MK>Повесить обработчик на раскрытие контекстное меню и уже в зависимости от кого раскрывается, добавлять нужные menu item'ы.

То, что Вы предлагаете, это понятно. Но это не выход. А если мне захочется создать кнопку, например? Или, программно сдвинуть GridSplitter?
Есть общая задача: научиться получать шаблон контента при переключении таб контрола.
Я думаю, у этой задачи должно быть решение.
Re[7]: [WPF] Получение уже примененного DataTemplate у TabCo
От: MxKazan Португалия  
Дата: 20.02.09 12:38
Оценка:
Здравствуйте, lsoft, Вы писали:

L>То, что Вы предлагаете, это понятно. Но это не выход. А если мне захочется создать кнопку, например? Или, программно сдвинуть GridSplitter?

L>Есть общая задача: научиться получать шаблон контента при переключении таб контрола.
L>Я думаю, у этой задачи должно быть решение.
Эта задача неверная. Если хочется менять шаблон, делайте его программно. Другой вариант делать в XAML некоторую основу визуального дерева и, при переключении вкладок, модифицировать ее и присваивать как ContentTemplate выбранной вкладки.
Re[8]: [WPF] Получение уже примененного DataTemplate у TabCo
От: lsoft  
Дата: 20.02.09 12:47
Оценка:
Здравствуйте, MxKazan, Вы писали:

MK>Эта задача неверная. Если хочется менять шаблон, делайте его программно. Другой вариант делать в XAML некоторую основу визуального дерева и, при переключении вкладок, модифицировать ее и присваивать как ContentTemplate выбранной вкладки.


Не будем спорить, пусть каждый остается при своем мнении. Если сделать моим способом не получится, разумеется, придется искать другие варианты.

Спасибо за диалог! :)
Re[6]: [WPF] Получение уже примененного DataTemplate у TabCo
От: MxKazan Португалия  
Дата: 20.02.09 12:47
Оценка:
Здравствуйте, lsoft, Вы писали:

L>Получилось сделать что-то похожее. Если в TabControl.Items добавлять объекты своих классов (Class1,Class2) и предоставить TabControl'у возможность самому искать подходящий шаблон из ресурсов, то таб контрол начинает отображать одинаковое содержимое как в заголовке вкладки, так и в содержимом вкладки.

L>Мне удалось находить шаблон в ЗАГОЛОВКЕ вкладки и менять его. Привожу код.
L>Осталось только научиться находить объект шаблона для СОДЕРЖИМОГО вкладки.
Честно говоря, я уже плохо понимаю о чем речь, но прочитав, возникло мнение, что вы запутались с моделью содержимого TabControl. Дело в том, что хоть TabItem и является ContentControl'ом, но фактически ее содержимое светится как содержимое TabControl, в шаблоне которого есть отдельный ContentPresenter. В одном из ваших примеров кода идет поиск в ContentPresenter'е TabItem'а, а надо искать в ContentPresenter'е TabControl'а. Зовется он PART_SelectedContentHost. Также есть свойства SelectedContent*.
Re[7]: [WPF] Получение уже примененного DataTemplate у TabCo
От: lsoft  
Дата: 20.02.09 12:57
Оценка:
Здравствуйте, MxKazan, Вы писали:

MK>Честно говоря, я уже плохо понимаю о чем речь, но прочитав, возникло мнение, что вы запутались с моделью содержимого TabControl. Дело в том, что хоть TabItem и является ContentControl'ом, но фактически ее содержимое светится как содержимое TabControl, в шаблоне которого есть отдельный ContentPresenter. В одном из ваших примеров кода идет поиск в ContentPresenter'е TabItem'а, а надо искать в ContentPresenter'е TabControl'а. Зовется он PART_SelectedContentHost. Также есть свойства SelectedContent*.


1) да, вероятно, уже запутался :)
2) SelectedContent* все равны нулю, за исключением SelectedContent который содержит мой Class1(2)
3) про PART_SelectedContentHost мне известно:

void c0_GotFocus(object sender, RoutedEventArgs e)
        {
            DataTemplateKey key = new DataTemplateKey((sender as TabItem).Content.GetType());

            DataTemplate fooTemplate = base.FindResource(key) as DataTemplate;

            ContentControl contentCtrl = (sender as TabItem) as ContentControl;

            var contentPresenter = this.tabControl.Template.FindName(
                "PART_SelectedContentHost",
                this.tabControl)
                    as ContentPresenter;

            //var contentPresenter = FindVisualChild<ContentPresenter>(
            //    //this.tabControl);
            //    (sender as TabItem));

            var _def1 = fooTemplate.FindName("def1", contentPresenter);
            var _def2 = fooTemplate.FindName("def2", contentPresenter);

            if (_def1 != null)
                (_def1 as Label).Content = new Random().Next(); 
            
            if (_def2 != null)
                (_def2 as Label).Content = new Random().Next();
        }


удивительно, но этот код падает с аналогичной ошибкой, но не сразу. удается перейти на вкладку другого типа, но вернуться на первую вкладку не получается — ошибка. в общем, этот код срабатывает только при первом открытии вкладки.

спасибо еще раз за разъяснения.
Re[8]: [WPF] Получение уже примененного DataTemplate у TabCo
От: lsoft  
Дата: 20.02.09 13:08
Оценка:
В общем, господа, делайте выводы. Вот такой код падает на втором переключении:



void c0_GotFocus(object sender, RoutedEventArgs e)
        {

                DataTemplateKey key = new DataTemplateKey((sender as TabItem).Content.GetType());

                DataTemplate fooTemplate = base.FindResource(key) as DataTemplate;

                //ContentControl contentCtrl = (sender as TabItem) as ContentControl;
                
                var contentPresenter = this.tabControl.Template.FindName(
                    "PART_SelectedContentHost",
                    this.tabControl)
                        as ContentPresenter;

                //var contentPresenter = FindVisualChild<ContentPresenter>(
                //    //this.tabControl);
                //    (sender as TabItem));

                //DataTemplateKey key = new DataTemplateKey((sender as TabItem).Content.GetType());
                //DataTemplate fooTemplate = this.tabControl.FindResource(key) as DataTemplate;

                var _def1 = fooTemplate.FindName("def1", contentPresenter);
                var _def2 = fooTemplate.FindName("def2", contentPresenter);

                if (_def1 != null)
                    (_def1 as Label).Content = new Random().Next(); 
                
                if (_def2 != null)
                    (_def2 as Label).Content = new Random().Next();


        }



зато такой работает




void c0_GotFocus(object sender, RoutedEventArgs e)
        {
            this.Dispatcher.BeginInvoke(DispatcherPriority.ApplicationIdle, (DispatcherOperationCallback)delegate(object unused)
            {

                DataTemplateKey key = new DataTemplateKey((sender as TabItem).Content.GetType());

                DataTemplate fooTemplate = base.FindResource(key) as DataTemplate;

                //ContentControl contentCtrl = (sender as TabItem) as ContentControl;
                
                var contentPresenter = this.tabControl.Template.FindName(
                    "PART_SelectedContentHost",
                    this.tabControl)
                        as ContentPresenter;

                //var contentPresenter = FindVisualChild<ContentPresenter>(
                //    //this.tabControl);
                //    (sender as TabItem));

                //DataTemplateKey key = new DataTemplateKey((sender as TabItem).Content.GetType());
                //DataTemplate fooTemplate = this.tabControl.FindResource(key) as DataTemplate;

                var _def1 = fooTemplate.FindName("def1", contentPresenter);
                var _def2 = fooTemplate.FindName("def2", contentPresenter);

                if (_def1 != null)
                    (_def1 as Label).Content = new Random().Next(); 
                
                if (_def2 != null)
                    (_def2 as Label).Content = new Random().Next();

                return null;
            }
            , null);


        }


:) :) хе-хе... пойду пифка выпью и фильмец посмотрю, так как НЕ ПО НИ МА Ю.
Re: [WPF] Получение уже примененного DataTemplate у TabContr
От: Vladek Россия Github
Дата: 21.02.09 20:29
Оценка:
Здравствуйте, lsoft, Вы писали:

L>Хочется получить этот темплейт ПЕРЕД показом другой вкладки, чтобы провести в нем некоторые изменения на лету.


Чем не угодили DataTrigger-ы?

L>Пожалуйста, посмотрите проект

L>http://rapidshare.com/files/200315961/WpfApplication4.zip.html
L>MD5: DC14AC4BD0C09413BA335D99F85DF0D5
Developers, developers, developers, developers, developers, developers, developers... © Steve Ballmer
Re[2]: [WPF] Получение уже примененного DataTemplate у TabCo
От: lsoft  
Дата: 26.02.09 07:46
Оценка:
Здравствуйте, Vladek, Вы писали:

V>Здравствуйте, lsoft, Вы писали:

L>>Хочется получить этот темплейт ПЕРЕД показом другой вкладки, чтобы провести в нем некоторые изменения на лету.
V>Чем не угодили DataTrigger-ы?

простите, не понимаю, как DataTrigger мне может помочь в осуществлении моего коварного плана по получению DataTemplate
Re[3]: [WPF] Получение уже примененного DataTemplate у TabCo
От: Codechanger Россия  
Дата: 26.02.09 08:16
Оценка: +1
Здравствуйте, lsoft, Вы писали:

L>Здравствуйте, Vladek, Вы писали:


V>>Здравствуйте, lsoft, Вы писали:

L>>>Хочется получить этот темплейт ПЕРЕД показом другой вкладки, чтобы провести в нем некоторые изменения на лету.
Вообще, такие вещи, как содержимое какого-нить меню, лучше хранить не в темплейте, а в коллекции какой-нить. Тогда у вас сразу эта проблема отпадет.
Re: [WPF] Получение уже примененного DataTemplate у TabContr
От: lsoft  
Дата: 26.02.09 08:39
Оценка:
Проблема решена, сердечное спасибо всем, кто принял посильное участие.
Суть проблемы была в том, что во время переключения вкладки в ТабКонтроле, не всегда удавалось найти примененный дататемплейт. Иногда удавалось, иногда нет. Как оказалось, это происходило из-за того, что к моменту вызова TabItem.GotFocus к PART_SelectedContentHost НЕ ВСЕГДА применялся новый ДатаТемплейт.
в результате работает примерно следующее:


        void c0_GotFocus(object sender, RoutedEventArgs e)
        {

            DataTemplateKey key = new DataTemplateKey((sender as TabItem).Content.GetType());

            DataTemplate fooTemplate = base.FindResource(key) as DataTemplate;

            var PART_SelectedContentHost = this.tabControl.Template.FindName(
                "PART_SelectedContentHost",
                this.tabControl)
                    as ContentPresenter;

            PART_SelectedContentHost.ApplyTemplate(); //АРХИВАЖНО!

            var _def1 = fooTemplate.FindName("def1", PART_SelectedContentHost);
            var _def2 = fooTemplate.FindName("def2", PART_SelectedContentHost);

            if (_def1 != null)
                (_def1 as Label).Content = new Random().Next();

            if (_def2 != null)
                (_def2 as Label).Content = new Random().Next();



        }


причем строка, помеченая АРХИВАЖНОЙ архиважно только в том случае, если вы пытаетесь искать ДатаТемплейт ВО ВРЕМЯ переключения вкладки, а не после завершения этого процесса.

Спасибо!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.