Silverlight Tips - Controls fixes

Jan Holan       07.04.2011       Silverlight, Komponenty       11007 zobrazení

V tomto článku "Controls fixes" neboli opravy standartních Silverlight controlů uvedu některé nedokonalosti controlů DataGrid, ComboBox, TabControl, DataPager a TextBox a u každého popíši jak je odstranit.

ExtendedDataGrid

Ve standartním DataGrid controlu není vyvolávána událost MouseLeftButtonDown  (MouseLeftButtonUp i MouseRightButtonDown fungují správně). Napíšeme si control ExtendedDataGrid zděděný z System.Windows.Controls.DataGrid a událost opravíme.

/// <summary>
/// Fixes DataGrid <c>MouseLeftButtonDown</c> event
/// </summary>
public class ExtendedDataGrid : System.Windows.Controls.DataGrid
{
    #region delegate and events
    /// <summary>
    /// MouseLeftButtonDown event
    /// </summary>
    public new event MouseButtonEventHandler MouseLeftButtonDown;
    #endregion

    #region constructors and destructors
    /// <summary>
    /// ExtendedDataGrid control constructor
    /// </summary>
    public ExtendedDataGrid() : base()
    {
        base.AddHandler(FrameworkElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(Base_MouseLeftButtonDown), true);
    }
    #endregion

    #region private member functions
    private void Base_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        this.OnMouseLeftButtonDown(e);
    }

    /// <summary>
    /// Raise MouseLeftButtonDown event
    /// </summary>
    protected new virtual void OnMouseLeftButtonDown(MouseButtonEventArgs e)
    {
        if (MouseLeftButtonDown != null)
        {
            MouseLeftButtonDown(this, e);
        }
    }
    #endregion
}

Kód nejprve deklarací události MouseLeftButtonDown s použitím klíčového slova new zavádí novou událost (stejného jména), která zastíní událost původní. Tuto událost pak v konstruktoru zaregistrujeme metodou AddHandler s parametrem handledEventsToo = true, tím je událost vyvolávána i když je již v její obsluze nastaveno Handled na true.

ExtendedComboBox

ComboBox standartně klávesou Enter otevírá dropdown seznam. To se nemusí obecně hodit např. když potřebujeme, aby klávesa enter potvrdila dialog s výběrem z comba apod. Pro otevírání ComboBoxu ponecháme pouze klávesu mezerník.

/// <summary>
/// Fixed ComboBox
/// </summary>
public class ExtendedComboBox : System.Windows.Controls.ComboBox
{
    /// <summary>
    /// Overrides ComboBox.OnKeyDown
    /// </summary>
    protected override void OnKeyDown(KeyEventArgs e)
    {
        if (e.Key == Key.Enter && !this.IsDropDownOpen)
        {
            return;
        }

        base.OnKeyDown(e);
    }
}

Naše třída ExtendedComboBox mění OnKeyDown controlu System.Windows.Controls.ComboBox, ze kterého je třída poděděna. V metodě je pak odchycen stisk klávesy enter, který je pak ignorován. Je zde ale nutné ještě podmínkou podle IsDropDownOpen rozlišit pouze stav, kdy není dropdown seznam ComboBoxu otevřen.

ExtendedTabControl

U controlu System.Windows.Controls.TabControl je problém s xml Language vlastností, ta je totiž do contentu TabItemů převzata z nadřazených elementů (např. přímo z RootVisual) pouze pro první tab. Zavedeme tedy třídu ExtendedTabControl, kde provedeme potřebné nastavení Language vlastnosti v události load.

/// <summary>
/// Fixes UI Language setting for tabs content
/// </summary>
public class ExtendedTabControl : System.Windows.Controls.TabControl
{
    #region constructors and destructors
    /// <summary>
    /// ExtendedTabControl control constructor
    /// </summary>
    public ExtendedTabControl()
    {
        this.Loaded += new System.Windows.RoutedEventHandler(ExtendedTabControl_Loaded);
    }
    #endregion

    #region private member functions
    private void ExtendedTabControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
    {
        foreach (object item in this.Items)
        {
            var tabItem = item as System.Windows.Controls.TabItem;
            if (tabItem != null && tabItem.Content != null)
            {
                ((FrameworkElement)tabItem.Content).Language = this.Language;
            }
        }
    }
    #endregion
}

DataPager

Control DataPager (z assembly System.Windows.Controls.Data.dll) má jeden problém, jsou v něm texty jako "Page", "of" které jsou natvrdo zakódovány anglicky a nepodporují lokalizaci. Vytvoříme odvozenou třídu, kde control počeštíme.

/// <summary>
/// Data pager control with localized labels 
/// </summary>
public class DataPager : System.Windows.Controls.DataPager
{
    #region member varible and default property initialization
    private TextBlock currentPagePrefixTextBlock;
    private TextBlock currentPageSuffixTextBlock;
    #endregion

    #region constructors and destructors
    /// <summary>
    /// DataPager control constructor
    /// </summary>
    public DataPager() : base()
    {
        this.PageIndexChanged += new EventHandler<EventArgs>(CustomDataPager_PageIndexChanged);
        this.MouseLeftButtonDown += new MouseButtonEventHandler(CustomDataPager_MouseLeftButtonDown);
    }
    #endregion

    #region action methods
    /// <summary>
    /// Overrides OnApplyTemplate
    /// </summary>
    public override void OnApplyTemplate()
    {
        base.OnApplyTemplate();

        currentPagePrefixTextBlock = (TextBlock)GetTemplateChild("CurrentPagePrefixTextBlock");
        currentPageSuffixTextBlock = (TextBlock)GetTemplateChild("CurrentPageSuffixTextBlock");
        currentPageSuffixTextBlock.SizeChanged += new SizeChangedEventHandler(currentPageSuffixTextBlock_SizeChanged);

        var currentPageTextBox = (TextBox)GetTemplateChild("CurrentPageTextBox");
        currentPageTextBox.TextChanged += new TextChangedEventHandler(currentPageTextBox_TextChanged);
    }
    #endregion

    #region private member functions
    private void currentPageTextBox_TextChanged(object sender, TextChangedEventArgs e)
    {
        TranslateLabels();
    }

    private void CustomDataPager_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    { 
        TranslateLabels();
    }

    private void CustomDataPager_PageIndexChanged(object sender, EventArgs e)
    {
        TranslateLabels();
    }

    private void currentPageSuffixTextBlock_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        TranslateLabels();
    }

    private void TranslateLabels()
    {
        if (currentPagePrefixTextBlock != null)
        {
            currentPagePrefixTextBlock.Text = "Strana"; //Properties.Resources.Page;
            currentPageSuffixTextBlock.Text = string.Format("/ {0}", this.PageCount);
        }
    }
    #endregion
}

V metodě OnApplyTemplate jsou načteny reference na vnitřní prvky CurrentPagePrefixTextBlock a CurrentPageSuffixTextBlock, těm je pak na události PageIndexChanged, MouseLeftButtonDown, SizeChanged a TextChanged na CurrentPageTextBoxu nastavován jiný text. V kódu je nový český text nastaven také natvrdo, nyní nám ale nic nebrání vytáhnout potřebný text do vlastního resource souboru.

ExtendedTextBox

Controly v Silverlight nemají na rozdíl od WPF vlastnost Focusable, pouze vlastnost IsTabStop. Nelze tedy např. nastavit, aby do controlu šlo vstoupit myší, ale přitom aby byl vynechán při procházení Tab klávesou. Toto může obecně vadit i jinde než u TextBoxu, u něj se ale nabízí udělat úpravu, aby pokud je TextBox nastaven na ReadOnly, byl přístup klávesou Tab potlačen (ale myší ne). Implementaci napíšeme do controlu ExtendedTextBox.

/// <summary>
/// TextBox with IsTabStop property settings automatic by IsReadOnly value
/// </summary>
public class ExtendedTextBox : System.Windows.Controls.TextBox
{
    #region member varible and default property initialization
    private bool m_NewIsTabStop;
    #endregion

    #region constructors and destructors
    /// <summary>
    /// ExtendedTextBox control constructor
    /// </summary>
    public ExtendedTextBox()
    {
        m_NewIsTabStop = base.IsTabStop;
        SetTabStop();
    }
    #endregion

    #region property getters/setters
    /// <summary>
    /// Gets or sets a value that indicates whether a control is included in tab navigation.
    /// </summary>
    public new bool IsTabStop
    {
        get { return m_NewIsTabStop; }
        set
        {
            m_NewIsTabStop = value;
            SetTabStop();
        }
    }

    /// <summary>
    /// IsReadOnly dependency property
    /// </summary>
    public new static readonly DependencyProperty IsReadOnlyProperty = DependencyProperty.Register("IsReadOnly", typeof(bool), typeof(ExtendedTextBox), new PropertyMetadata(OnIsReadOnlyPropertyChanged));

    /// <summary>
    /// Gets or sets the value that determines if the user can change the text in the text box.
    /// </summary>
    public new bool IsReadOnly
    {
        get { return (bool)base.GetValue(IsReadOnlyProperty); }
        set { base.SetValue(IsReadOnlyProperty, value); }
    }

    private static void OnIsReadOnlyPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs args)
    {
        var extendedTextBox = sender as ExtendedTextBox;
        extendedTextBox.OnIsReadOnlyChanged((bool)args.OldValue, (bool)args.NewValue);
    }

    /// <summary>
    /// IsReadOnly changed
    /// </summary>
    protected virtual void OnIsReadOnlyChanged(bool oldValue, bool newValue)
    {
        base.IsReadOnly = newValue;
        SetTabStop();
    }
    #endregion

    #region private member functions
    /// <summary>
    /// Overrides OnMouseLeftButtonDown
    /// </summary>
    protected override void OnMouseLeftButtonDown(System.Windows.Input.MouseButtonEventArgs e)
    {
        base.IsTabStop = true;
        base.OnMouseLeftButtonDown(e);
    }

    /// <summary>
    /// Overrides OnLostFocus
    /// </summary>
    protected override void OnLostFocus(System.Windows.RoutedEventArgs e)
    {
        SetTabStop();
        base.OnLostFocus(e);
    }

    private void SetTabStop()
    {
        base.IsTabStop = m_NewIsTabStop && !this.IsReadOnly;
    }
    #endregion
}

Kód podle vlastnosti IsReadOnly a původního nastavení IsTabStop mění tuto vlastnost na novou hodnotu (v metodě SetTabStop). Pokud se ale do controlu vstupuje kliknutím myší (událost OnMouseLeftButtonDown), je vlastnost opět zapnuta a na LostFocus opět přenastavena. Výsledkem je tedy chování, že při nastavení IsReadOnly na true pak TextBox klávesou Tab focus nedostává, ale pokud potřebujeme myší např. označit jeho aktuální text (pro jeho zkopírování), je to umožněno.

 

hodnocení článku

0 bodů / 1 hlasů       Hodnotit mohou jen registrované uživatelé.

 

Nový příspěvek

 

                       
Nadpis:
Antispam: Komu se občas házejí perly?
Příspěvek bude publikován pod identitou   anonym.

Nyní zakládáte pod článkem nové diskusní vlákno.
Pokud chcete reagovat na jiný příspěvek, klikněte na tlačítko "Odpovědět" u některého diskusního příspěvku.

Nyní odpovídáte na příspěvek pod článkem. Nebo chcete raději založit nové vlákno?

 

  • Administrátoři si vyhrazují právo komentáře upravovat či mazat bez udání důvodu.
    Mazány budou zejména komentáře obsahující vulgarity nebo porušující pravidla publikování.
  • Pokud nejste zaregistrováni, Vaše IP adresa bude zveřejněna. Pokud s tímto nesouhlasíte, příspěvek neodesílejte.

přihlásit pomocí externího účtu

přihlásit pomocí jména a hesla

Uživatel:
Heslo:

zapomenuté heslo

 

založit nový uživatelský účet

zaregistrujte se

 
zavřít

Nahlásit spam

Opravdu chcete tento příspěvek nahlásit pro porušování pravidel fóra?

Nahlásit Zrušit

Chyba

zavřít

feedback