1

I wrote custom TreeView control.

XAML:

<TreeView x:Class="EArchiveMaster.View.MyTreeView"
             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" 
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="300">
    <TreeView.ItemContainerStyle>
        <Style TargetType="{x:Type TreeViewItem}">
            <Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
            <Setter Property="IsSelected" Value="{Binding IsSelected, Mode=TwoWay}" />
            <EventSetter Event="LostFocus" Handler="EventSetter_OnHandler" />
        </Style>
    </TreeView.ItemContainerStyle>
</TreeView>

.cs

public partial class MyTreeView
{
    public event Action SomeItemLostFocus;

    public MyTreeView()
    {
        InitializeComponent();
    }

    private void EventSetter_OnHandler(object sender, RoutedEventArgs e)
    {
        e.Handled = true;
        if (SomeItemLostFocus != null)
            SomeItemLostFocus();
    }
}

But when I try to use it I got well known error:

Cannot set Name attribute value 'TextBox' on element 'TextBox'. 'TextBox' is under the scope of element 'MyTreeView', which already had a name registered when it was defined in another scope.

I found some receipts how to fix this error. Namely, specify .xaml part of control in its code-behind. But I have no idea how can I do this.

monstr
  • 1,680
  • 1
  • 25
  • 44
  • There's already an identical [question for WPF](http://stackoverflow.com/questions/751325/how-to-create-a-wpf-usercontrol-with-named-content). Most answers will apply in your case too. Also, you didn't show where you set `Name` to `TextBox`. – icebat Nov 28 '14 at 07:59
  • @icebat It is does't matter where I set `Name` to `TextBox`. The question is 'How to specify .xaml part of control in its code-behind'. Can you help? – monstr Nov 28 '14 at 08:02
  • @icebat I have seen that question, but I don't understand what should I write in `OnInitialized` method in my case... – monstr Nov 28 '14 at 08:12

2 Answers2

1

The code clearly shows you want to extend TreeView. Basically if you want to build control that can hold some content(which can be named...), like ContentControl, ItemsControl, etc.. it is always better to go with CustomControl. UserControl with XAML and CS code is not suitable for this case.

In your case, create a class like below and extend the functionalities,

public class MyTreeView : TreeView
{
    public event Action SomeItemLostFocus;

    public MyTreeView()
    {
        DefaultStyleKey = typeof(MyTreeView);
    }

    public override void OnLostFocus(object sender, RoutedEventArgs e)
    {
        e.Handled = true;
        if (SomeItemLostFocus != null)
            SomeItemLostFocus();
    }
}

If you want to customize the look and feel, you should override the default Style of the control. This style should be available in generic.xaml file inside Themes folder. More information on Custom Control development is here.

     <Style TargetType="{x:Type TreeViewItem}">
        <Setter Property="IsExpanded"
                Value="{Binding IsExpanded, Mode=TwoWay}" />
        <Setter Property="IsSelected"
                Value="{Binding IsSelected, Mode=TwoWay}" />
    </Style>
Jawahar
  • 4,775
  • 1
  • 24
  • 47
  • Did you see what is the question? Because, you almost just copy-pasted my code – monstr Nov 28 '14 at 09:05
  • No. Your code is UserControl. My code is CustomControl. Your code have partial class along with XAML. My code have stand alone class extended from TreeView. My code will not throw the NameScope error. – Jawahar Nov 28 '14 at 09:10
  • ok got it, but your style does not contain ``. This string is very important. If `Style` will be defined separately from .cs I will not be able to use `EventSetter` cause it requires event handler. – monstr Nov 28 '14 at 09:18
  • Instead of using Eventhandler, you can use OnLostFocus override method. – Jawahar Nov 28 '14 at 09:19
  • just don't understand how your `OnLostFocus` method will be called, when LostFocus event of `TreeViewITEM` will rise. Where is the link between them? – monstr Nov 28 '14 at 09:22
1

I found solution appropriate for me. This it the way how to define Style of TreeViewItem in code, not in XAML. Now I have TreeView definded only in code-behind, therefore, error will not be risen.

public class MyTreeView : TreeView
{
    public event RoutedEventHandler ItemLostLogicFocus;

    protected override void OnInitialized(EventArgs e)
    {
        base.OnInitialized(e);

        var itemContainerStyle = new Style
        {
            TargetType = typeof(TreeViewItem),
        };

        #region Binding

        var expandedBinding = new Binding("IsExpanded")
        {
            Mode = BindingMode.TwoWay,
        };

        var selectedBinding = new Binding("IsSelected")
        {
            Mode = BindingMode.TwoWay,
        };

        #endregion

        #region Setters

        itemContainerStyle.Setters.Add(new Setter
        {
            Property = TreeViewItem.IsExpandedProperty,
            Value = expandedBinding
        });
        itemContainerStyle.Setters.Add(new Setter
        {
            Property = TreeViewItem.IsSelectedProperty,
            Value = selectedBinding
        });

        #endregion

        #region EventSetters

        itemContainerStyle.Setters.Add(new EventSetter
        {
            Event = LostFocusEvent,
            Handler = new RoutedEventHandler(ItemLostLogicFocusHandler)

        });

        #endregion

        ItemContainerStyle = itemContainerStyle;
    }

    private void ItemLostLogicFocusHandler(Object sender, RoutedEventArgs e)
    {
        e.Handled = true;
        if (ItemLostLogicFocus != null)
            ItemLostLogicFocus(sender, e);
    }
}
monstr
  • 1,680
  • 1
  • 25
  • 44