How do I update the Cell edit control?

9 posts, 0 answers
  1. Jan Paolo
    Jan Paolo avatar
    26 posts
    Member since:
    Sep 2018

    Posted 24 Apr Link to this post

    Hi, I'm trying to create a class deriving from the GridViewDataColumn. For example, I'd like to make the FontWeight Bold based on the bounded value. I was able to do it successfully for the view control by overriding CreateCellElement. So I thought overriding CreateCellEditElement would be the other method of the edit control but it is not working as expected. Attached is an illustration of the issue. Also, I have a sample project but I can't attach it here.

    Thanks

    Jan

  2. Jan Paolo
    Jan Paolo avatar
    26 posts
    Member since:
    Sep 2018

    Posted 24 Apr Link to this post

    Attached is my sample project. Please change the extension from .gif to .zip
  3. Jan Paolo
    Jan Paolo avatar
    26 posts
    Member since:
    Sep 2018

    Posted 24 Apr in reply to Jan Paolo Link to this post

    Also, you might need to uninstall then install the Telerik.Windows.Controls.GridView.for.Wpf.Xaml nuget package to build successfully
  4. Vladimir Stoyanov
    Admin
    Vladimir Stoyanov avatar
    405 posts

    Posted 25 Apr Link to this post

    Hello Jan,

    Thank you for the provided sample project.

    The reason for the described behavior is that the CreateCellEditElement method is invoked too early. What I can suggest, is overriding the PrepareCellForEdit method of the GridViewDataColumn instead of the CreateCellEditElement method. It is called later than the CreateCellEditElement method and the TextBox editor will have its Text set by this point. 

    However, please note that you will also need to introduce some logic in order to restore the bolded/italicized text back to its string representation, when the cell gets out of edit mode. If the tags are removed when the cell enters edit mode, after the cells exits edit mode, the bound property string will not contain the tags. That said, I cannot say for certain whether this approach would work in all scenarios. 

    Hope this helps.

    Regards,
    Vladimir Stoyanov
    Progress Telerik
    Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
  5. Jan Paolo
    Jan Paolo avatar
    26 posts
    Member since:
    Sep 2018

    Posted 25 Apr Link to this post

    Hello Vladimir,

    Thanks for the immediate response. You're correct on overriding PrepareCellForEdit and restoring the tags to the string value. Below is the initial working prototype of my custom class if anybody is interested in trying or looking at it. Will most likely update it later after some refactoring.

    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using Telerik.Windows.Controls;
    using Telerik.Windows.Controls.GridView;
     
    namespace Demo1
    {
        internal sealed class HtmlGridViewDataColumn : GridViewDataColumn
        {
            public HtmlGridViewDataColumn()
            {
                Initialized += HtmlGridViewDataColumn_Initialized;
            }
     
            public override FrameworkElement CreateCellElement(GridViewCell cell, object dataItem) =>
                UpdateTextElement(base.CreateCellElement(cell, dataItem));
     
            public override object GetNewValueFromEditor(object editor)
            {
                var t = (TextBox)editor;
                if (t.FontWeight == FontWeights.Bold)
                    ApplyTag(TagName.Bold);
                if (t.FontStyle == FontStyles.Italic)
                    ApplyTag(TagName.Italic);
                return base.GetNewValueFromEditor(editor);
     
                void ApplyTag(string name)
                {
                    var html = new HtmlStringBuilder(t.Text);
                    if (!html.ContainsTag(name))
                        t.Text = new HtmlStringBuilder(t.Text)
                        .ToggleTag(name)
                        .ToString();
                }
            }
     
            protected override object PrepareCellForEdit(FrameworkElement editingElement, RoutedEventArgs editingEventArgs)
            {
                UpdateTextElement(editingElement);
                return base.PrepareCellForEdit(editingElement, editingEventArgs);
            }
     
            private static FrameworkElement UpdateTextElement(FrameworkElement element)
            {
                switch (element)
                {
                    case TextBlock t:
                        Update(new TextBlockAdapter(t));
                        break;
                    case TextBox t:
                        Update(new TextBoxAdapter(t));
                        break;
                }
     
                return element;
     
                void Update(ITextElementdapter adapter)
                {
                    adapter.FontWeight =
                        TryRemoveTag(TagName.Bold)
                        ? FontWeights.Bold
                        : FontWeights.Normal;
                    adapter.FontStyle =
                        TryRemoveTag(TagName.Italic)
                        ? FontStyles.Italic
                        : FontStyles.Normal;
     
                    bool TryRemoveTag(string name)
                    {
                        var html = new HtmlStringBuilder(adapter.Text);
                        var c = html.ContainsTag(name);
                        html.RemoveTag(name);
                        adapter.Text = html.ToString();
                        return c;
                    }
                }
            }
     
            private void HtmlGridViewDataColumn_Initialized(object sender, System.EventArgs e)
            {
                DataControl.BeginningEdit += DataControl_BeginningEdit;
                DataControl.CellEditEnded += DataControl_CellEditEnded;
            }
     
            private void DataControl_BeginningEdit(object sender, GridViewBeginningEditRoutedEventArgs e)
            {
                if (e.Cell.Column == this)
                    e.Cell.KeyDown += Cell_KeyDown;
            }
     
            private void DataControl_CellEditEnded(object sender, GridViewCellEditEndedEventArgs e)
            {
                if (e.Cell.Column == this)
                    e.Cell.KeyDown -= Cell_KeyDown;
            }
     
            private void Cell_KeyDown(object sender, KeyEventArgs e)
            {
                if (Keyboard.Modifiers == ModifierKeys.Control)
                {
                    if (e.Key == Key.B)
                    {
                        var textBox = GetTextBox();
                        textBox.FontWeight =
                            textBox.FontWeight == FontWeights.Bold
                            ? FontWeights.Normal
                            : FontWeights.Bold;
                    }
                    else if (e.Key == Key.I)
                    {
                        var textBox = GetTextBox();
                        textBox.FontStyle =
                            textBox.FontStyle == FontStyles.Italic
                            ? FontStyles.Normal
                            : FontStyles.Italic;
                    }
                    else if (e.Key == Key.Tab)
                    {
                        var textBox = GetTextBox();
                        var caretIndex = textBox.CaretIndex;
                        var tab = "\t";
                        textBox.AcceptsTab = true;
                        textBox.Text = textBox.Text.Insert(caretIndex, tab);
                        textBox.CaretIndex = caretIndex + tab.Length;
                        e.Handled = true;
                    }
     
                    TextBox GetTextBox()
                    {
                        var cell = (GridViewCell)sender;
                        return (TextBox)cell.GetEditingElement();
                    }
                }
            }
     
            private static class TagName
            {
                public static string Bold => "b";
     
                public static string Italic => "i";
            }
     
            private class HtmlStringBuilder
            {
                private string htmlString;
     
                public HtmlStringBuilder(string htmlString)
                {
                    this.htmlString = htmlString;
                }
     
                public HtmlStringBuilder ToggleTag(string name)
                {
                    var startTag = StartTag(name);
                    var endTag = EndTag(name);
                    if (ContainsTag(name))
                        RemoveTag(name);
                    else
                        htmlString = $"{startTag}{htmlString}{endTag}";
     
                    return this;
                }
     
                public HtmlStringBuilder RemoveTag(string name)
                {
                    htmlString = htmlString
                        ?.Replace(StartTag(name), "")
                        .Replace(EndTag(name), "");
                    return this;
                }
     
                public bool ContainsTag(string name) => htmlString.Contains(StartTag(name));
     
                private static string StartTag(string name) => $"<{name}>";
     
                private static string EndTag(string name) => StartTag($"/{name}");
     
                public override string ToString() => htmlString;
            }
     
            private interface ITextElementdapter
            {
                FontWeight FontWeight { get; set; }
     
                FontStyle FontStyle { get; set; }
     
                string Text { get; set; }
            }
     
            private class TextBoxAdapter : ITextElementdapter
            {
                private readonly TextBox textBox;
     
                public TextBoxAdapter(TextBox textBox)
                {
                    this.textBox = textBox;
                }
     
                public FontWeight FontWeight { get => textBox.FontWeight; set => textBox.FontWeight = value; }
     
                public FontStyle FontStyle { get => textBox.FontStyle; set => textBox.FontStyle = value; }
     
                public string Text { get => textBox.Text; set => textBox.Text = value; }
            }
     
            private class TextBlockAdapter : ITextElementdapter
            {
                private readonly TextBlock textBlock;
     
                public TextBlockAdapter(TextBlock textBlock)
                {
                    this.textBlock = textBlock;
                }
     
                public FontWeight FontWeight { get => textBlock.FontWeight; set => textBlock.FontWeight = value; }
     
                public FontStyle FontStyle { get => textBlock.FontStyle; set => textBlock.FontStyle = value; }
     
                public string Text { get => textBlock.Text; set => textBlock.Text = value; }
            }
        }
    }
  6. Jan Paolo
    Jan Paolo avatar
    26 posts
    Member since:
    Sep 2018

    Posted 25 Apr Link to this post

    Hello Vladimir, would you please give an example on how to unit test my custom control. I tried the following but it doesn't seem to work.

     
    using System.Windows.Controls;
    using System.Windows.Data;
    using Telerik.Windows.Controls.GridView;
    using Xunit;
     
    namespace Demo1
    {
        public class HtmlGridViewDataColumnTests
        {
            [WpfFact]
            public void Demo()
            {
                var column = new HtmlGridViewDataColumn
                {
                    DataMemberBinding = new Binding("Name")
                };
                var textBlock = (TextBlock)column.CreateCellElement(
                    new GridViewCell(),
                    new MyItem());
                Assert.Equal("a", textBlock.Text);
            }
        }
     
        public class MyItem
        {
            public string Name { get; set; } = "a";
        }
    }
  7. Vladimir Stoyanov
    Admin
    Vladimir Stoyanov avatar
    405 posts

    Posted 30 Apr Link to this post

    Hello Jan Paolo,

    Thank you for the update. 

    I am glad to hear that you were able to achieve what you were going for. Thank you for the sharing your solution with the community.

    As for unit testing, I would suggest testing the custom column with the RadGridView control, since the GridViewDataColumn, which it inherits is designed to be used in that way.

    Can you check out the testing infrastructure of the RadGridView by downloading its source code from your telerik account?  The test project is inside Controls\GridView\GridView.Tests directory. You can also open the GridView_WPF.sln in the Controls\GridView directory, which includes the test project. It includes many tests for different functionality, which may be helpful for your scenario.

    Please, check the test project out and let me know, if you find it helpful.

    Regards,
    Vladimir Stoyanov
    Progress Telerik
    Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
  8. Jan Paolo
    Jan Paolo avatar
    26 posts
    Member since:
    Sep 2018

    Posted 03 Jun Link to this post

    Hello Vladimir,

    Thanks for the suggestion. Unfortunately, I can't figure out how to sort the grid programatically and examine its sorted items for testing. There's a `GridViewDataControl.PerformSorting` method that's used in GridView.Tests but it's internal. Do you know any other way I can sort the grid in unit tests?
    ```

    using System.Windows.Data;

    using System.Windows.Interactivity

    using Telerik.Windows.Controls;

    using Telerik.Windows.Data;

    using Xunit;

     

    [WpfFact] // from Xunit.StaFact

    public void Test()

    {

       var grid = new RadGridView();

       grid.AutoGenerateColumns = false;

       grid.Columns.Add(new GridViewDataColumn { DataMemberBinding = new Binding() });

       grid.ItemsSource = new[] { 1, 3, 2 };

     

       Interaction.GetBehaviors(grid).Add(new CustomSortBehavior());

       /// TODO: perform sort

     

       Assert.Equal(new[] { 1, 2, 3}, grid.Items);

    }

     

    public class CustomSortBehavior : Behavior<RadGridView>

    {

        /// custom sort implementation

    }

    ```

  9. Vladimir Stoyanov
    Admin
    Vladimir Stoyanov avatar
    405 posts

    Posted 06 Jun Link to this post

    Hello Jan Paolo,

    Thank you for the provided code snippet. 

    Can you check out the Programmatic Sorting article in our documentation and let me know if you find it helpful?

    I am looking forward to your reply.

    Regards,
    Vladimir Stoyanov
    Progress Telerik
    Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
Back to Top