Domenica mattina, giornata uggiosa..., quasi quasi mi faccio un Adorner. smile_teeth

L'idea è quella di realizzare una demo che mostri come aggiungere delle informazioni di selezione (tipo designer) attorno al controllo che ha il focus e l'utilizzare un Adorner è sicuramente la soluzione migliore in quanto è un elemento associato a un UIElement che può renderizzare del contenuto in un layer che sta "sopra" lo UIElement associato, di fatto arricchendolo.

Step 1: Create l'adorner ereditando dalla classe Adorner

class MyAdorner:Adorner
    {
        /// <summary>
        /// Creates an adorner instance
        /// </summary>
        /// <param name="elementToAdorn">Element to adorn</param>
        public MyAdorner (UIElement elementToAdorn):base(elementToAdorn)
        {
        }
        protected override void OnRender (System.Windows.Media.DrawingContext drawingContext)
        {
            base.OnRender(drawingContext);

            //Get adorned element current size
            Rect adornedElementRect;
            FrameworkElement element=base.AdornedElement as FrameworkElement;
            if (element != null)
            {
                Size sz = new Size(element.ActualWidth, element.ActualHeight);
                adornedElementRect = new Rect(sz);
            }
            else
                adornedElementRect = new Rect(base.AdornedElement.DesiredSize);

            Pen pen = new Pen(SystemColors.ControlDarkDarkBrush, 1.5);
            double radius = 3.0;

            // Draw a circle at each corner.
            drawingContext.DrawEllipse(SystemColors.ControlBrush, pen, adornedElementRect.TopLeft, radius, radius);
            drawingContext.DrawEllipse(SystemColors.ControlBrush, pen, adornedElementRect.TopRight, radius, radius);
            drawingContext.DrawEllipse(SystemColors.ControlBrush, pen, adornedElementRect.BottomLeft, radius, radius);
            drawingContext.DrawEllipse(SystemColors.ControlBrush, pen, adornedElementRect.BottomRight, radius, radius);

        }
    }

Nell'override di OnRender, usando un approccio "alla GDI" disegno gli elementi di selezione usando la dimensione attuale del controllo come area di destinazione.

Step2: Recuperare il layer usato dall'adorner per renderizzare il proprio contenuto usando AdornerLayer.GetAdornerLayer: se siamo all'interno di una Window non dovremmo avere problemi in quanto tutto ciò che viene renderizzato è in realta contenuto in un AdornerLayer (se date un occhiata al VisualTree con Mole ve ne renderete subito conto) ma fate attenzione che in altre situazioni il layer potrebbe non essere presente, in questo caso dovete racchiudere il contenitore principale in un AdornerDecorator.
Una volta recuperato il layer aggiungere al layer il decorator, nel mio esempio memorizzo l'adorner attivo (activeAdorner) in modo da rimuoverlo quando un altro controllo viene selezionato.

private void AdornElement (FrameworkElement focusedElement)
{
    //Get adorner layer (if any), window exposes one by default
    AdornerLayer layer = AdornerLayer.GetAdornerLayer(focusedElement);
    if (layer != null)
    {
        //Unselect previous element
        if (activeAdorner != null) layer.Remove(activeAdorner);
        //Creates adorner
        activeAdorner = new MyAdorner(focusedElement);
        layer.Add(activeAdorner);
    }
    else
    {
        throw new ApplicationException("No adorner layer found");
        /*                 
         If no adorner found we can add one via xaml

        <AdornerDecorator>
          <DockPanel Name=“mainPane”>
          </DockPanel>
        </AdornerDecorator> 
        
        */
    }

Step 3: Una volta selezionato un controllo, far si che venga "evidenziato", nel mio caso lo faccio per semplicità nell'evento Click.

/// <summary>
/// Draw an adorner around active element
/// </summary>
private void SelectCurrentElement ()
{
    FrameworkElement focusedElement=FocusManager.GetFocusedElement(this) as FrameworkElement;
    if (focusedElement != null)
    {
        AdornElement(focusedElement);            
    }        
}
private void button1_Click (object sender, RoutedEventArgs e)
{
    SelectCurrentElement();
}

private void button2_Click (object sender, RoutedEventArgs e)
{
    SelectCurrentElement();
}

private void checkBox1_Checked (object sender, RoutedEventArgs e)
{
    SelectCurrentElement();
}

Lo xaml è talmente banale che ve lo risparmio... smile_regular il risultato è visibile nella figura che segue...

image




Nota 1:

Essendo l'adorner renderizzato "sopra" l'elemento associato, è quest'ultimo che riceve tutti gli eventi di input diretti al controllo che sta decorando, per renderlo insensibile bisogna impostare IsHitTestVisible=false.

Nota 2:
Silverlight 2.0 non supporta quanto raccontato in questo post...


Technorati Tags: ,,