none
Toggle Display Edit Mode with corresponding template

    Question

  • Hi,

    I have an itemscontrol that shows a list of phone numbers for a particular account.  I am able to get the phone numbers to display and get everything bound properly.  What I want to do is to make it so that when you click on a phone number, it switches templates and becomes editable.  I don't want to do this just using a textbox with readonly or anything like that.  I completely want to switch out the content for edit mode.  I've tried this several different ways and it's so very close to working but not quite there.  Here's where I'm at now.

    I created a custom control called EditPanel that subclasses ContentControl.

    Code Snippet

    public class EditPanel : ContentControl
        {
            private EditPanelItem _editItemPanel;

            static EditPanel()

            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(EditPanel), new FrameworkPropertyMetadata(typeof(EditPanel)));
            }

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

                _editItemPanel = new EditPanelItem();
                _editItemPanel.ItemDisplayContentTemplate = DisplayContentTemplate;
                _editItemPanel.ItemEditContentTemplate = EditContentTemplate;
               
                this.Content = _editItemPanel;
            }


    The rest of the class just specifies two attached properties (both ControlTemplate), EditContentTemplate and DisplayContentTemplate.  This class is basically just a wraper around EditPanelItem which is a user control that displays the content differently for edit and display modes.  It uses an IsInEditMode bool attached property to toggle the template for edit mode.  It also contains a <ContentControl /> in the Display and Edit ControlTemplates, and the ItemDisplayContentTemplate and ItemEditContentTemplate templates are set on the corresponding <ContentControl />s.

    Back to my ItemsControl. The DataTemplate looks like this:



     
    Code Snippet

               <DataTemplate x:Key="PhoneNumbersItemsTemplate">
                    <StackPanel Style="{StaticResource FormRowStackPanelStyle}">
                        <localControls:EditPanel>
                            <localControls:EditPanel.DisplayContentTemplate>
                                <ControlTemplate>

                                    <StackPanel Orientation="Horizontal">
                                        <Label Style="{StaticResource LabelStyle}" Content="{Binding Path=PhoneNumberTypeName}" />
                                        <Label Style="{StaticResource DisplayLabelStyle}" Content="{Binding Path=PhoneNumber1}" />
                                    </StackPanel>

                                </ControlTemplate>
                            </localControls:EditPanel.DisplayContentTemplate>
                            <localControls:EditPanel.EditContentTemplate>
                                <ControlTemplate>

                                    <StackPanel Orientation="Horizontal">
                                        <Label Style="{StaticResource LabelStyle}" Padding="0">
                                            <ComboBox ItemsSource="{Binding Source={x:Static utilities:Globals.PhoneNumberTypes}, Path=AllPhoneNumberTypes}"
                                        Style="{StaticResource FormValueComboBoxStyle}"
                                        ItemTemplate="{StaticResource FormComboDataTemplate}"
                                        SelectedValuePath="Value"
                                        SelectedValue="{Binding Path=PhoneNumberType}"
                                        />
                                        </Label>
                                        <TextBox Style="{StaticResource EditTextBox}" Text="{Binding Path=PhoneNumber1}" />
                                    </StackPanel>

                                </ControlTemplate>
                            </localControls:EditPanel.EditContentTemplate>
                        </localControls:EditPanel>
                    </StackPanel>
                </DataTemplate>






    The reason i used a custom control as a wrapper is so I could pass in named controls.  I really, really wish UserControl let you do this.  Anyway, the problem with my current solution is: after the first Item binds to the ItemsControl, all the rest show up blank.. Like the inner EditPanel control got wiped out after the first binding. BUT, this only happens when i have bound content inside my EditPanel's 2 ControlTemplates.  If I take out that bound content and replace it with something like:


    <StackPanel Background="Red"><Label>Here is some edit content!</Label></StackPanel>

    then ALL the items are bound properly, with a working EditPanel inside each ItemsControl Item.

    Anyone have any idea what kind of black magic is going on with my code?


    Thanks,
    Trevor
    Wednesday, April 09, 2008 10:08 PM

Answers

  • sidenote: this forum tool is absolutely frustring.  when trying to use mark code block i completely lost a massive reply i had been writing. this happens often. mac firefox.


    anyway, i got the problem fixed.  i can't use Keith's solution because of what i explained above:

     Trevor Hartman wrote:
    This was the approach I started with, but realized I actually need 4 templates.  2 Outer Templates and 2 Inner Templates.  Let me show you what it should look like:


    http://i299.photobucket.com/albums/mm300/trevorhartman/phone-list.jpg


    The office phone is how it looks in Display Mode.
    The Home Phone is how it looks in Edit mode.

    I have a Display outer template that wraps its content in a white border and has a mouseover effect.
    I have an Edit outer template that wraps its content in the grey border and places Save/Cancel/Delete buttons below it.

    The inner content needs to be passed into the EditPanel control, because the EditPanel could be used to edit an Address for example.  The Edit mode of address would have TextBoxes for Adress1, Address2 city state etc..




    I did end up simplifying the previous setup into a single custom control that sublcasses ContentControl.  here's how it turned out (not using mark code block tool for risk of losing post):

    Oh and the reason it wasn't binding correctly was it didn't like my nested templates.  instead of specifying inline, i extracted the two templates and gave them a key to refer to as a staticresource


    XAML:

    <!-- Phone Number -->
                <ControlTemplate x:Key="PhoneEditContentTemplate">
                    <StackPanel Orientation="Horizontal">
                        <Label Style="{StaticResource LabelStyle}" Padding="0">
                            <ComboBox ItemsSource="{Binding Source={xTongue Tiedtatic utilities:Globals.PhoneNumberTypes}, Path=AllPhoneNumberTypes}"
                                        Style="{StaticResource FormValueComboBoxStyle}"
                                        ItemTemplate="{StaticResource FormComboDataTemplate}"
                                        SelectedValuePath="Value"
                                        SelectedValue="{Binding Path=PhoneNumberType}"
                                        />
                        </Label>
                        <TextBox Style="{StaticResource EditTextBox}" Text="{Binding Path=PhoneNumber1}" />
                    </StackPanel>
                </ControlTemplate>
                <ControlTemplate x:Key="PhoneDisplayContentTemplate">
                   
                    <StackPanel Orientation="Horizontal">
                        <Label Style="{StaticResource LabelStyle}" Content="{Binding Path=PhoneNumberTypeName}" />
                        <Label Style="{StaticResource DisplayLabelStyle}" Content="{Binding Path=PhoneNumber1}" />
                    </StackPanel>
                   
                </ControlTemplate>
                <DataTemplate x:Key="PhoneNumbersItemsTemplate">
                   
                    <StackPanel Style="{StaticResource FormRowStackPanelStyle}">
                        <localControls:EditPanel EditContentTemplate="{StaticResource PhoneEditContentTemplate}"
                                                 DisplayContentTemplate="{StaticResource PhoneDisplayContentTemplate}"
                        />
                    </StackPanel>
                   
                </DataTemplate>




    EditPanel class:

        public class EditPanel : ContentControl
        {
            private ContentControl _innerContent;
            public ContentControl InnerContent
            {
                get { return _innerContent; }
                set { _innerContent = value; }
            }

            private ControlTemplate _displayTemplate;
            private ControlTemplate _editTemplate;

            public event EventHandler Cancel;
            public event EventHandler Save;
            public event EventHandler Delete;

            static EditPanel()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(EditPanel), new FrameworkPropertyMetadata(typeof(EditPanel)));
            }

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

                _innerContent = new ContentControl();
                _innerContent.Template = EditContentTemplate;
               
                this.Content = _innerContent;

                _displayTemplate = this.FindResource("EditPanelDisplayTemplate") as ControlTemplate;
                _editTemplate = this.FindResource("EditPanelEditTemplate") as ControlTemplate;

                this.Style = this.FindResource("EditPanelStyle") as Style;
            }

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

                if (this.Template == _displayTemplate)
                {
                    _innerContent.Template = DisplayContentTemplate;

                    // Wire up events for display template
                    Border displayBorder = _displayTemplate.FindName("RowBorder", this) as Border;
                    displayBorder.MouseDown += new MouseButtonEventHandler(displayBorder_MouseDown);
                }
                else if (this.Template == _editTemplate)
                {
                    _innerContent.Template = EditContentTemplate;

                    // Wire up events for edit template
                    Button saveButton = _editTemplate.FindName("Save", this) as Button;
                    Button cancelButton = _editTemplate.FindName("Cancel", this) as Button;
                    Button deleteButton = _editTemplate.FindName("Delete", this) as Button;

                    cancelButton.Click += new RoutedEventHandler(cancelButton_Click);
                    saveButton.Click += new RoutedEventHandler(saveButton_Click);
                    deleteButton.Click += new RoutedEventHandler(deleteButton_Click);
                }

            }

           
           
            #region Event Handlers
            void deleteButton_Click(object sender, RoutedEventArgs e)
            {
                if (Delete != null) Delete(this, e);
            }
            void saveButton_Click(object sender, RoutedEventArgs e)
            {
                if (Save != null) Save(this, e);
            }
            void cancelButton_Click(object sender, RoutedEventArgs e)
            {
                if (Cancel != null) Cancel(this, e);
                IsInEditMode = false;
            }

            void displayBorder_MouseDown(object sender, MouseButtonEventArgs e)
            {
                IsInEditMode = true;
            }

            #endregion


            #region Custom Events
            protected virtual void OnCancel(EventArgs e)
            {
                if (Cancel != null) Cancel(this, e);
            }
            protected virtual void OnDelete(EventArgs e)
            {
                if (Delete != null) Delete(this, e);
            }
            protected virtual void OnSave(EventArgs e)
            {
                if (Save != null) Save(this, e);
            }
            #endregion


            /// <summary>
            /// Gets/sets dipslay/edit mode
            /// </summary>
            public bool IsInEditMode
            {
                get { return (bool)GetValue(IsInEditModeProperty); }
                set { SetValue(IsInEditModeProperty, value); }
            }
            /// <summary>
            /// Represents the IsInEditMode property.
            /// </summary>
            public static readonly DependencyProperty IsInEditModeProperty =
                DependencyProperty.Register("IsInEditMode", typeof(bool), typeof(ContentControl), new UIPropertyMetadata(false, OnIsInEditModeChanged));

            static void OnIsInEditModeChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
            {
                ContentControl control = depObj as ContentControl;
               
            }



            /// <summary>
            /// DisplayContentTemplate
            /// </summary>
            public ControlTemplate DisplayContentTemplate
            {
                get { return (ControlTemplate)GetValue(DisplayContentTemplateProperty); }
                set { SetValue(DisplayContentTemplateProperty, value); }
            }
            public static readonly DependencyProperty DisplayContentTemplateProperty =
                DependencyProperty.Register("DisplayContentTemplate", typeof(ControlTemplate), typeof(ContentControl), new UIPropertyMetadata(null, OnDisplayContentTemplateChanged));
            static void OnDisplayContentTemplateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
            {
            }

            /// <summary>
            /// EditContentTemplate
            /// </summary>
            public ControlTemplate EditContentTemplate
            {
                get { return (ControlTemplate)GetValue(EditContentTemplateProperty); }
                set { SetValue(EditContentTemplateProperty, value); }
            }
            public static readonly DependencyProperty EditContentTemplateProperty =
                DependencyProperty.Register("EditContentTemplate", typeof(ControlTemplate), typeof(ContentControl), new UIPropertyMetadata(null, OnEditContentTemplateChanged));

            static void OnEditContentTemplateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
            {
            }

        }


    thanks, trevor

    Friday, April 11, 2008 7:29 PM

All replies

  • I would think it would be easier to just create two separate templates (one for the editable state and one for the non-editable state) and use a style trigger to apply the desired template to the Item in your ItemsControl.

     

    Hope this helps,

     

    Keith

     

     

    Thursday, April 10, 2008 1:23 AM
  • Hey, thanks for the reply.  This was the approach I started with, but realized I actually need 4 templates.  2 Outer Templates and 2 Inner Templates.  Let me show you what it should look like:


    http://i299.photobucket.com/albums/mm300/trevorhartman/phone-list.jpg


    The office phone is how it looks in Display Mode.
    The Home Phone is how it looks in Edit mode.

    I have a Display outer template that wraps its content in a white border and has a mouseover effect.
    I have an Edit outer template that wraps its content in the grey border and places Save/Cancel/Delete buttons below it.

    The inner content needs to be passed into the EditPanel control, because the EditPanel could be used to edit an Address for example.  The Edit mode of address would have TextBoxes for Adress1, Address2 city state etc..

    That is why my custom control exposes two properties to pass in 2 ControlTemplates.  Still strugling with how to get this right.


    Thanks, Trevor
    Thursday, April 10, 2008 5:14 PM
  • Hi Trevor,

    Keith's solution In my humble opinion is more elegant than your current approach, would you please further elaborate why it's not an option for you?

    Thanks
    Friday, April 11, 2008 4:16 AM
  • sidenote: this forum tool is absolutely frustring.  when trying to use mark code block i completely lost a massive reply i had been writing. this happens often. mac firefox.


    anyway, i got the problem fixed.  i can't use Keith's solution because of what i explained above:

     Trevor Hartman wrote:
    This was the approach I started with, but realized I actually need 4 templates.  2 Outer Templates and 2 Inner Templates.  Let me show you what it should look like:


    http://i299.photobucket.com/albums/mm300/trevorhartman/phone-list.jpg


    The office phone is how it looks in Display Mode.
    The Home Phone is how it looks in Edit mode.

    I have a Display outer template that wraps its content in a white border and has a mouseover effect.
    I have an Edit outer template that wraps its content in the grey border and places Save/Cancel/Delete buttons below it.

    The inner content needs to be passed into the EditPanel control, because the EditPanel could be used to edit an Address for example.  The Edit mode of address would have TextBoxes for Adress1, Address2 city state etc..




    I did end up simplifying the previous setup into a single custom control that sublcasses ContentControl.  here's how it turned out (not using mark code block tool for risk of losing post):

    Oh and the reason it wasn't binding correctly was it didn't like my nested templates.  instead of specifying inline, i extracted the two templates and gave them a key to refer to as a staticresource


    XAML:

    <!-- Phone Number -->
                <ControlTemplate x:Key="PhoneEditContentTemplate">
                    <StackPanel Orientation="Horizontal">
                        <Label Style="{StaticResource LabelStyle}" Padding="0">
                            <ComboBox ItemsSource="{Binding Source={xTongue Tiedtatic utilities:Globals.PhoneNumberTypes}, Path=AllPhoneNumberTypes}"
                                        Style="{StaticResource FormValueComboBoxStyle}"
                                        ItemTemplate="{StaticResource FormComboDataTemplate}"
                                        SelectedValuePath="Value"
                                        SelectedValue="{Binding Path=PhoneNumberType}"
                                        />
                        </Label>
                        <TextBox Style="{StaticResource EditTextBox}" Text="{Binding Path=PhoneNumber1}" />
                    </StackPanel>
                </ControlTemplate>
                <ControlTemplate x:Key="PhoneDisplayContentTemplate">
                   
                    <StackPanel Orientation="Horizontal">
                        <Label Style="{StaticResource LabelStyle}" Content="{Binding Path=PhoneNumberTypeName}" />
                        <Label Style="{StaticResource DisplayLabelStyle}" Content="{Binding Path=PhoneNumber1}" />
                    </StackPanel>
                   
                </ControlTemplate>
                <DataTemplate x:Key="PhoneNumbersItemsTemplate">
                   
                    <StackPanel Style="{StaticResource FormRowStackPanelStyle}">
                        <localControls:EditPanel EditContentTemplate="{StaticResource PhoneEditContentTemplate}"
                                                 DisplayContentTemplate="{StaticResource PhoneDisplayContentTemplate}"
                        />
                    </StackPanel>
                   
                </DataTemplate>




    EditPanel class:

        public class EditPanel : ContentControl
        {
            private ContentControl _innerContent;
            public ContentControl InnerContent
            {
                get { return _innerContent; }
                set { _innerContent = value; }
            }

            private ControlTemplate _displayTemplate;
            private ControlTemplate _editTemplate;

            public event EventHandler Cancel;
            public event EventHandler Save;
            public event EventHandler Delete;

            static EditPanel()
            {
                DefaultStyleKeyProperty.OverrideMetadata(typeof(EditPanel), new FrameworkPropertyMetadata(typeof(EditPanel)));
            }

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

                _innerContent = new ContentControl();
                _innerContent.Template = EditContentTemplate;
               
                this.Content = _innerContent;

                _displayTemplate = this.FindResource("EditPanelDisplayTemplate") as ControlTemplate;
                _editTemplate = this.FindResource("EditPanelEditTemplate") as ControlTemplate;

                this.Style = this.FindResource("EditPanelStyle") as Style;
            }

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

                if (this.Template == _displayTemplate)
                {
                    _innerContent.Template = DisplayContentTemplate;

                    // Wire up events for display template
                    Border displayBorder = _displayTemplate.FindName("RowBorder", this) as Border;
                    displayBorder.MouseDown += new MouseButtonEventHandler(displayBorder_MouseDown);
                }
                else if (this.Template == _editTemplate)
                {
                    _innerContent.Template = EditContentTemplate;

                    // Wire up events for edit template
                    Button saveButton = _editTemplate.FindName("Save", this) as Button;
                    Button cancelButton = _editTemplate.FindName("Cancel", this) as Button;
                    Button deleteButton = _editTemplate.FindName("Delete", this) as Button;

                    cancelButton.Click += new RoutedEventHandler(cancelButton_Click);
                    saveButton.Click += new RoutedEventHandler(saveButton_Click);
                    deleteButton.Click += new RoutedEventHandler(deleteButton_Click);
                }

            }

           
           
            #region Event Handlers
            void deleteButton_Click(object sender, RoutedEventArgs e)
            {
                if (Delete != null) Delete(this, e);
            }
            void saveButton_Click(object sender, RoutedEventArgs e)
            {
                if (Save != null) Save(this, e);
            }
            void cancelButton_Click(object sender, RoutedEventArgs e)
            {
                if (Cancel != null) Cancel(this, e);
                IsInEditMode = false;
            }

            void displayBorder_MouseDown(object sender, MouseButtonEventArgs e)
            {
                IsInEditMode = true;
            }

            #endregion


            #region Custom Events
            protected virtual void OnCancel(EventArgs e)
            {
                if (Cancel != null) Cancel(this, e);
            }
            protected virtual void OnDelete(EventArgs e)
            {
                if (Delete != null) Delete(this, e);
            }
            protected virtual void OnSave(EventArgs e)
            {
                if (Save != null) Save(this, e);
            }
            #endregion


            /// <summary>
            /// Gets/sets dipslay/edit mode
            /// </summary>
            public bool IsInEditMode
            {
                get { return (bool)GetValue(IsInEditModeProperty); }
                set { SetValue(IsInEditModeProperty, value); }
            }
            /// <summary>
            /// Represents the IsInEditMode property.
            /// </summary>
            public static readonly DependencyProperty IsInEditModeProperty =
                DependencyProperty.Register("IsInEditMode", typeof(bool), typeof(ContentControl), new UIPropertyMetadata(false, OnIsInEditModeChanged));

            static void OnIsInEditModeChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e)
            {
                ContentControl control = depObj as ContentControl;
               
            }



            /// <summary>
            /// DisplayContentTemplate
            /// </summary>
            public ControlTemplate DisplayContentTemplate
            {
                get { return (ControlTemplate)GetValue(DisplayContentTemplateProperty); }
                set { SetValue(DisplayContentTemplateProperty, value); }
            }
            public static readonly DependencyProperty DisplayContentTemplateProperty =
                DependencyProperty.Register("DisplayContentTemplate", typeof(ControlTemplate), typeof(ContentControl), new UIPropertyMetadata(null, OnDisplayContentTemplateChanged));
            static void OnDisplayContentTemplateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
            {
            }

            /// <summary>
            /// EditContentTemplate
            /// </summary>
            public ControlTemplate EditContentTemplate
            {
                get { return (ControlTemplate)GetValue(EditContentTemplateProperty); }
                set { SetValue(EditContentTemplateProperty, value); }
            }
            public static readonly DependencyProperty EditContentTemplateProperty =
                DependencyProperty.Register("EditContentTemplate", typeof(ControlTemplate), typeof(ContentControl), new UIPropertyMetadata(null, OnEditContentTemplateChanged));

            static void OnEditContentTemplateChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
            {
            }

        }


    thanks, trevor

    Friday, April 11, 2008 7:29 PM