views:

699

answers:

2

I have been strugging with this for a while, it would have been simple to do in WindowForms.

I am making a IRC Client, there will be a number of Tabs one for each channel connect to. Each Tab needs to show a number of things, UserList, MessageHistory, Topic.

In WindowForms i would just have inherited from TabItem, added some Custom Properties, and Controls, and done.

In WPF i am having some slight issues with working out how to do it.

I have tried many ways of doing it, and below is my current method, but i cannot get the TextBox to bind to the Topic Property.

    <Style TargetType="{x:Type t:IRCTabItem}" BasedOn="{StaticResource {x:Type TabItem}}" >
    <Setter Property="ContentTemplate">
        <Setter.Value>
            <DataTemplate>
                <Grid>
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="540" />
                        <ColumnDefinition />
                    </Grid.ColumnDefinitions>
                    <StackPanel Grid.Column="0">
                        <TextBox Text="{Binding Topic, RelativeSource={RelativeSource AncestorType={x:Type t:IRCTabItem}}}" />   
                    </StackPanel>
                </Grid>
            </DataTemplate>              
        </Setter.Value>
    </Setter>
</Style>

And the Codebehind

 public class IRCTabItem : TabItem
    {
        static IRCTabItem()
        {
            //This OverrideMetadata call tells the system that this element wants to provide a style that is different than its base class.
            //This style is defined in themes\generic.xaml
            //DefaultStyleKeyProperty.OverrideMetadata(typeof(IRCTabItem),
             //   new FrameworkPropertyMetadata(typeof(IRCTabItem)));
        }

        public static readonly RoutedEvent CloseTabEvent =
            EventManager.RegisterRoutedEvent("CloseTab", RoutingStrategy.Bubble,
                typeof(RoutedEventHandler), typeof(IRCTabItem));

        public event RoutedEventHandler CloseTab
        {
            add { AddHandler(CloseTabEvent, value); }
            remove { RemoveHandler(CloseTabEvent, value); }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            Button closeButton = base.GetTemplateChild("PART_Close") as Button;
            if (closeButton != null)
                closeButton.Click += new System.Windows.RoutedEventHandler(closeButton_Click);
        }

        void closeButton_Click(object sender, System.Windows.RoutedEventArgs e)
        {
            this.RaiseEvent(new RoutedEventArgs(CloseTabEvent, this));
        }

        public bool Closeable
        {
            get { return (bool)GetValue(CloseableProperty); }
            set { SetValue(CloseableProperty, value); }
        }
        public static readonly DependencyProperty CloseableProperty = DependencyProperty.Register("Closeable", typeof(bool), typeof(IRCTabItem), new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));


        public List<String> UserList
        {
            get { return (List<string>)GetValue(UserListProperty); }
            set { SetValue(UserListProperty, value); }
        }
        public static readonly DependencyProperty UserListProperty = DependencyProperty.Register("UserList", typeof(List<String>), typeof(IRCTabItem), new FrameworkPropertyMetadata(new List<String>(), FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public String Topic
        {
            get { return (string)GetValue(TopicProperty); }
            set { SetValue(TopicProperty, value); }
        }
        public static readonly DependencyProperty TopicProperty = DependencyProperty.Register("Topic", typeof(String), typeof(IRCTabItem), new FrameworkPropertyMetadata("Not Connected", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

        public bool HasAlerts
        {
            get { return (bool)GetValue(HasAlertsProperty); }
            set { SetValue(HasAlertsProperty, value); }
        }
        public static readonly DependencyProperty HasAlertsProperty = DependencyProperty.Register("HasAlerts", typeof(bool), typeof(IRCTabItem), new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));


    }

So my questions are:

Am i doing it the right way (best practices)? If so how can i bind DataTemplate to Properties? If not so, what is the correct way of achieve what i am trying to achieve?

Edit 1: Added Peter Stephens Suggestions Edit 2: Added Visible Edit Summary Edit 3: Tags

A: 

One problem you have is that your dependency properties are implemented incorrectly.

Topic should be implemented like so:

public String Topic 
{ 
    get { return (string) GetValue(TopicProperty); }
    set { SetValue(TopicProperty, value); }
}
public static readonly DependencyProperty TopicProperty = 
    DependencyProperty.Register("Topic", typeof(String), 
        typeof(IRCTabItem), new FrameworkPropertyMetadata("Not Connected", 
        FrameworkPropertyMetadataOptions.BindsTwoWayByDefault));

You need to treat the CLR property as syntactic sugar surrounding the WPF dependency property. Let the WPF property handle the storage of the value, otherwise you will effectively have two properties with two different values.

Peter Stephens
Thanks for that, its good to know :)
lloydsparkes
A: 

I believe your binding expression is missing the Mode for relative source.

Try this:

<TextBox Text="{Binding Topic, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type t:IRCTabItem}}}" />

The default for Mode is null so if you don't include that setting things probably won't work out so well.

Peter Stephens
Thanks for your reply. I have added this modification, and it makes no change :(
lloydsparkes