none
Toolbar Flotante - Similar a la de office 2007 RRS feed

  • Pregunta

  • hola

    La consulta apunta si alguien conoce como implementar una toolbar simiar a la que usa Office 2007

    como esta imagen

     

    Segun pude ver usando la clase Popup se puede lograr, pero no he podido encontrle la vuelta aun

    por ahi si alguien sabe como usar esta clase para unirla al toolbar, seria de ayuda al menos una pista

     

    Tambien note que los ejemplos del msdn sobre el popup se han eliminado Popup Samples

    por ahi si alguien sabria de que otro sitio se podrian descargar

     

    saludos


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina
    viernes, 23 de abril de 2010 14:05

Respuestas

  • Hola Leandro.

    Por los ejemplos, supongo que es para WPF. Yo personalmente no usaria un control Popup ya que solo te va a servir para una pagina concreta, en su lugar me crearia un UserControl de modo que se pueda vincular desde donde quieras y lo tengas mejor modulado.

    Ademas, mediante un UserControl vas a poder personalizar mas el control aplicando estilos propios y/o redefiniendo los existentes.

    Te he modificado un codigo que tenia para hacer algo similar, el codigo es un poco basico pero te puede servir como referencia para mejorarlo y adaptarlo a tus necesidades.

    Para el codigo he creado en la ventana principal un RitchTextBox para realizar la prueba, de modo que tendra un grid como el siguiente:

    codigo xaml:

     

     <Grid Name="mainGrid" Background="#FFFEFEFE">
      <RichTextBox Height="287" HorizontalAlignment="Left" Margin="12,12,0,0" 
          Name="richTextBox1" VerticalAlignment="Top" Width="479" 
          SelectionChanged="richTextBox1_SelectionChanged" />
     </Grid>

     

    En el codigo .cs de la ventana principal se crea el UserControl y se controla el evento de seleccion de texto, con un codigo como el siguiente:

    codigo .cs de la ventana principal

     

     public partial class MainWindow : Window
     {
      private PopupTextControl m_popup;
    
      public MainWindow()
      {
       InitializeComponent();
    
       // crear el control y añadirlo al grid principal (oculto)   
       m_popup = new PopupTextControl();
       m_popup.Visibility = System.Windows.Visibility.Hidden;
       mainGrid.Children.Add(m_popup);
      }
    
    
      private void richTextBox1_SelectionChanged(object sender, RoutedEventArgs e)
      {
       // Si existe seleccion, hacer visible para modificar la selecciony y pasarsela
       if (richTextBox1.Selection.IsEmpty == false)
       {
        m_popup.Visibility = System.Windows.Visibility.Visible;
        m_popup.Selection = richTextBox1.Selection;
       }
       // si NO existe seleccion, invisible el popup
       else m_popup.Visibility = System.Windows.Visibility.Hidden;
      }
     }
    }

     

    el control de usuario se puede ver que se llama 'PopupTextControl', el cual se inicializa y se oculta al iniciarse la clase.

    Para el control de usuario he realizado lo siguiente:

    codigo xaml del control de usuario:

     

    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	x:Class="OfficePopup.PopupTextControl"
    	x:Name="mainControl"
    	UseLayoutRounding="True"
    	d:DesignWidth="640" d:DesignHeight="480" Width="210" 
     Height="110" Loaded="UserControl_Loaded" Opacity="0.4"
     MouseEnter="mainControl_MouseEnter" MouseLeave="mainControl_MouseLeave">
    
    	<Border x:Name="border" BorderBrush="Black" BorderThickness="1" Margin="0,0,-2,-2" Width="200" Height="100" 
       Background="#FFFEFEFE">
    		<Border.Effect>
    			<DropShadowEffect/>
    		</Border.Effect>
    
    		<Grid x:Name="LayoutRoot" Margin="0">
    			<Grid.RowDefinitions>
    				<RowDefinition/>
    				<RowDefinition/>
    			</Grid.RowDefinitions>
    			<ComboBox x:Name="cb_fonts" Margin="8,8,8,8" SelectionChanged="cb_fonts_SelectionChanged" />
    			<Button x:Name="btn_n" Content="N" HorizontalAlignment="Left" Margin="8,8,0,8" Grid.Row="1" Width="34" FontWeight="Bold" Click="Button_Click" />
       <Button x:Name="btn_k" Content="K" HorizontalAlignment="Left" Margin="46,8,0,8" Grid.Row="1" Width="34" FontStyle="Italic" Click="Button_Click"/>
       <Button x:Name="btn_r" Content="R" HorizontalAlignment="Left" Margin="84,8,0,8" Grid.Row="1" Width="34" Click="Button_Click"/>
    		</Grid>
    	</Border>
    </UserControl>

     

    He puesto el codigo completo para que se vean los eventos al entrar y salir de el para controlar la opacidad del mismo.

    el codigo .cs del control de usuario:

     

    namespace OfficePopup
    {
    	/// <summary>
    	/// Interaction logic for popupText.xaml
    	/// </summary>
    	public partial class PopupTextControl : UserControl
    	{
      /// <summary>
      /// Constructor por defecto de la clase
      /// </summary>
      public PopupTextControl()
    		{
    			this.InitializeComponent();
    		}
    
      #region [enventos]
    
      /// <summary>
      /// evento ocurrido al cargar el UserControl
      /// </summary>
      /// <param name="sender">objeto remitente</param>
      /// <param name="e">argumentos del evento</param>
      private void UserControl_Loaded(object sender, RoutedEventArgs e)
      {
       // obtener la lista de fuentes disponibles
       cb_fonts.ItemsSource = Fonts.SystemFontFamilies;   
      }
    
      /// <summary>
      /// Evento ocurrido al entrar con el raton en el control de usuario
      /// </summary>
      /// <param name="sender">objeto remitente</param>
      /// <param name="e">argumentos del evento</param>
      private void mainControl_MouseEnter(object sender, MouseEventArgs e)
      {
       mainControl.Opacity = 1.0;
      }
    
      /// <summary>
      /// Evento ocurrido al salir con el raton del control de usuario
      /// </summary>
      /// <param name="sender">objeto remitente</param>
      /// <param name="e">argumentos del evento</param>
      private void mainControl_MouseLeave(object sender, MouseEventArgs e)
      {
    
       mainControl.Opacity = 0.4;
      }
    
      /// <summary>
      /// eventos de botones
      /// </summary>
      /// <param name="sender">objeto remitente</param>
      /// <param name="e">argumentos del evento</param>
      private void Button_Click(object sender, RoutedEventArgs e)
      {
       Button btn = (Button)e.OriginalSource;
       switch (btn.Name)
       {
        case "btn_n":
         this.Selection.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);     
         break;
    
        case "btn_k":
         this.Selection.ApplyPropertyValue(TextElement.FontStyleProperty, FontStyles.Italic);     
         break;
    
        case "btn_r":
         this.Selection.ApplyPropertyValue(TextElement.FontStyleProperty, FontStyles.Normal);
         this.Selection.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Normal);
         break;
       }
    
      }
    
      /// <summary>
      /// Evento ocurrido al cambiar de seleccion un combo
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void cb_fonts_SelectionChanged(object sender, SelectionChangedEventArgs e)
      {
       ComboBox combo = (ComboBox)e.OriginalSource;
       switch (combo.Name)
       {
        case "cb_fonts":
         this.Selection.ApplyPropertyValue(TextElement.FontFamilyProperty, combo.SelectedValue);
         break;
    
        // TODO: incluir otros combos ....
       }
      }
    
      #endregion [enventos]
    
    
      #region [Propiedades]
    
      /// <summary>
      /// Propiedad para establecer u obtener la seleccion a tratar
      /// </summary>
      public TextSelection Selection { set; get; }
    
      #endregion [Propiedades]
     }
    }

     

    Del mismo modo, he dejado el codigo entero para que no existan problemas al vincular con el .xaml

    En este codigo del control de usuario, tan solo obtiene la referencia a la seleccion y modifica sus propiedades estableciendolas en el RichTextBox.

    He intentado simplificar el ejemplo para que se tome como base de una posible implementacion, de modo que existen detallitos que no se han tomado en cuenta (como al hacer click en el UserControl se borra la seleccion, aunque no se elimina la referencia y se puede ir cambiando).

    Bueno, espero que sirva como guia basica, si existe algun problema en el codigo comentarmenlo.

     


    Saludos
    David González
    miércoles, 28 de abril de 2010 10:23

Todas las respuestas

  • ¡Hola!

    No sé si te podrá ayudar pero aqui tienes un ejemplo de cómo se maneja la clase popup:

    http://eldoctorando.serveblog.net:8080/?p=944

    Saludos.

    martes, 27 de abril de 2010 15:02
  • hola

    gracias por la respuesta

    si esta interesante el ejemplo, voy a analizarlo

     

    de paso queria comentar que encontre varios ejemplso de codigo que podrian ser utiles

    http://code.msdn.microsoft.com/wpfsamples

    segun vi parecieran sera los que se eliminaron de los ejemplos del msdn

     

    Una consulta aunque sean pago el componente, no sabes de alguno que simule bastante parecido la funcionalidad de esa toolbar que actua en la seleccion, como usa el Office 2007

    me parece raro que ninguna empresa implemetnara algo similar, con ese efecto de opacidad ciando uno se acerca o leja con el mouse de la toolbar contectual

    saludos


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina
    miércoles, 28 de abril de 2010 3:11
  • ¡Buenos días Leandro!

     

    La empresa que conozco que mejor desarrolla controles, a mi humilde juicio, y que más se le parecen a los de microsoft es: http://www.infragistics.com/

    Pero de todas formas, con un poco de paciencia y como guía el código expuesto en la página que te he comentado, no debe de ser complejo poder llegar a hacer lo que deseas.

    El problema que le veo es que si el usuario aumenta el zoom del explorador el punto de llamada del control al menú contextual se ve modificado y ya no aparece donde se le llamó en un principio. Se soluciona pudiendo controlar el zoom del explorador. Cosa que hasta el momento no he conseguido.

    Ahhh, se me olvidaba. Gracias por el aporte de los ejemplos de código.

    Si descubro algo más, no dudes que te lo postearé.

     

    Un saludo,

     

    miércoles, 28 de abril de 2010 8:16
  • Hola Leandro.

    Por los ejemplos, supongo que es para WPF. Yo personalmente no usaria un control Popup ya que solo te va a servir para una pagina concreta, en su lugar me crearia un UserControl de modo que se pueda vincular desde donde quieras y lo tengas mejor modulado.

    Ademas, mediante un UserControl vas a poder personalizar mas el control aplicando estilos propios y/o redefiniendo los existentes.

    Te he modificado un codigo que tenia para hacer algo similar, el codigo es un poco basico pero te puede servir como referencia para mejorarlo y adaptarlo a tus necesidades.

    Para el codigo he creado en la ventana principal un RitchTextBox para realizar la prueba, de modo que tendra un grid como el siguiente:

    codigo xaml:

     

     <Grid Name="mainGrid" Background="#FFFEFEFE">
      <RichTextBox Height="287" HorizontalAlignment="Left" Margin="12,12,0,0" 
          Name="richTextBox1" VerticalAlignment="Top" Width="479" 
          SelectionChanged="richTextBox1_SelectionChanged" />
     </Grid>

     

    En el codigo .cs de la ventana principal se crea el UserControl y se controla el evento de seleccion de texto, con un codigo como el siguiente:

    codigo .cs de la ventana principal

     

     public partial class MainWindow : Window
     {
      private PopupTextControl m_popup;
    
      public MainWindow()
      {
       InitializeComponent();
    
       // crear el control y añadirlo al grid principal (oculto)   
       m_popup = new PopupTextControl();
       m_popup.Visibility = System.Windows.Visibility.Hidden;
       mainGrid.Children.Add(m_popup);
      }
    
    
      private void richTextBox1_SelectionChanged(object sender, RoutedEventArgs e)
      {
       // Si existe seleccion, hacer visible para modificar la selecciony y pasarsela
       if (richTextBox1.Selection.IsEmpty == false)
       {
        m_popup.Visibility = System.Windows.Visibility.Visible;
        m_popup.Selection = richTextBox1.Selection;
       }
       // si NO existe seleccion, invisible el popup
       else m_popup.Visibility = System.Windows.Visibility.Hidden;
      }
     }
    }

     

    el control de usuario se puede ver que se llama 'PopupTextControl', el cual se inicializa y se oculta al iniciarse la clase.

    Para el control de usuario he realizado lo siguiente:

    codigo xaml del control de usuario:

     

    <UserControl
    	xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    	xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    	xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    	xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    	mc:Ignorable="d"
    	x:Class="OfficePopup.PopupTextControl"
    	x:Name="mainControl"
    	UseLayoutRounding="True"
    	d:DesignWidth="640" d:DesignHeight="480" Width="210" 
     Height="110" Loaded="UserControl_Loaded" Opacity="0.4"
     MouseEnter="mainControl_MouseEnter" MouseLeave="mainControl_MouseLeave">
    
    	<Border x:Name="border" BorderBrush="Black" BorderThickness="1" Margin="0,0,-2,-2" Width="200" Height="100" 
       Background="#FFFEFEFE">
    		<Border.Effect>
    			<DropShadowEffect/>
    		</Border.Effect>
    
    		<Grid x:Name="LayoutRoot" Margin="0">
    			<Grid.RowDefinitions>
    				<RowDefinition/>
    				<RowDefinition/>
    			</Grid.RowDefinitions>
    			<ComboBox x:Name="cb_fonts" Margin="8,8,8,8" SelectionChanged="cb_fonts_SelectionChanged" />
    			<Button x:Name="btn_n" Content="N" HorizontalAlignment="Left" Margin="8,8,0,8" Grid.Row="1" Width="34" FontWeight="Bold" Click="Button_Click" />
       <Button x:Name="btn_k" Content="K" HorizontalAlignment="Left" Margin="46,8,0,8" Grid.Row="1" Width="34" FontStyle="Italic" Click="Button_Click"/>
       <Button x:Name="btn_r" Content="R" HorizontalAlignment="Left" Margin="84,8,0,8" Grid.Row="1" Width="34" Click="Button_Click"/>
    		</Grid>
    	</Border>
    </UserControl>

     

    He puesto el codigo completo para que se vean los eventos al entrar y salir de el para controlar la opacidad del mismo.

    el codigo .cs del control de usuario:

     

    namespace OfficePopup
    {
    	/// <summary>
    	/// Interaction logic for popupText.xaml
    	/// </summary>
    	public partial class PopupTextControl : UserControl
    	{
      /// <summary>
      /// Constructor por defecto de la clase
      /// </summary>
      public PopupTextControl()
    		{
    			this.InitializeComponent();
    		}
    
      #region [enventos]
    
      /// <summary>
      /// evento ocurrido al cargar el UserControl
      /// </summary>
      /// <param name="sender">objeto remitente</param>
      /// <param name="e">argumentos del evento</param>
      private void UserControl_Loaded(object sender, RoutedEventArgs e)
      {
       // obtener la lista de fuentes disponibles
       cb_fonts.ItemsSource = Fonts.SystemFontFamilies;   
      }
    
      /// <summary>
      /// Evento ocurrido al entrar con el raton en el control de usuario
      /// </summary>
      /// <param name="sender">objeto remitente</param>
      /// <param name="e">argumentos del evento</param>
      private void mainControl_MouseEnter(object sender, MouseEventArgs e)
      {
       mainControl.Opacity = 1.0;
      }
    
      /// <summary>
      /// Evento ocurrido al salir con el raton del control de usuario
      /// </summary>
      /// <param name="sender">objeto remitente</param>
      /// <param name="e">argumentos del evento</param>
      private void mainControl_MouseLeave(object sender, MouseEventArgs e)
      {
    
       mainControl.Opacity = 0.4;
      }
    
      /// <summary>
      /// eventos de botones
      /// </summary>
      /// <param name="sender">objeto remitente</param>
      /// <param name="e">argumentos del evento</param>
      private void Button_Click(object sender, RoutedEventArgs e)
      {
       Button btn = (Button)e.OriginalSource;
       switch (btn.Name)
       {
        case "btn_n":
         this.Selection.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Bold);     
         break;
    
        case "btn_k":
         this.Selection.ApplyPropertyValue(TextElement.FontStyleProperty, FontStyles.Italic);     
         break;
    
        case "btn_r":
         this.Selection.ApplyPropertyValue(TextElement.FontStyleProperty, FontStyles.Normal);
         this.Selection.ApplyPropertyValue(TextElement.FontWeightProperty, FontWeights.Normal);
         break;
       }
    
      }
    
      /// <summary>
      /// Evento ocurrido al cambiar de seleccion un combo
      /// </summary>
      /// <param name="sender"></param>
      /// <param name="e"></param>
      private void cb_fonts_SelectionChanged(object sender, SelectionChangedEventArgs e)
      {
       ComboBox combo = (ComboBox)e.OriginalSource;
       switch (combo.Name)
       {
        case "cb_fonts":
         this.Selection.ApplyPropertyValue(TextElement.FontFamilyProperty, combo.SelectedValue);
         break;
    
        // TODO: incluir otros combos ....
       }
      }
    
      #endregion [enventos]
    
    
      #region [Propiedades]
    
      /// <summary>
      /// Propiedad para establecer u obtener la seleccion a tratar
      /// </summary>
      public TextSelection Selection { set; get; }
    
      #endregion [Propiedades]
     }
    }

     

    Del mismo modo, he dejado el codigo entero para que no existan problemas al vincular con el .xaml

    En este codigo del control de usuario, tan solo obtiene la referencia a la seleccion y modifica sus propiedades estableciendolas en el RichTextBox.

    He intentado simplificar el ejemplo para que se tome como base de una posible implementacion, de modo que existen detallitos que no se han tomado en cuenta (como al hacer click en el UserControl se borra la seleccion, aunque no se elimina la referencia y se puede ir cambiando).

    Bueno, espero que sirva como guia basica, si existe algun problema en el codigo comentarmenlo.

     


    Saludos
    David González
    miércoles, 28 de abril de 2010 10:23
  • David,

    la verdad te pasaste, estoy probando el control y esta genial, se controla mucho mejor que el Popup que ya viene e WPF

    ahora lo estoy adaptando un poco y abusando de tu amabilidad por ahi me puedas dar un tip de donde mirar para hacer lo siguiente:

    el tema es el posicionamiento, necesitaria que aparezca situado como lo hace el toolbar flotanto de office, del mouse a la derecha y arriba
    lo que no veo es como dentro del user control cambiar este posicionamiento de forma contextual donde debe aparecer, imagino que no sera simple

    saludos


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina
    miércoles, 28 de abril de 2010 19:36
  • Hola Leandro.

    realmente si siempre fuese en la misma posicion, si que es simple, pero el problema va a ser que en caso de no caber, se muestre debajo o hacia la izquierda, que es algo que se debe de comprobar con algunas condiciones.

    basicamente se puede hacer lo siguiente (como siempre esto es una muestra y se debe optimizar)

    Basandonos en el codigo anterior y teniendo en cuenta que el contenedor principal es un grid y no un canvas, se debe especificar la alineacion del popup al crearlo, como en el siguiente codigo:

    // crear el control y añadirlo al grid principal (oculto)      
    m_popup = new PopupTextControl();
    m_popup.Visibility = System.Windows.Visibility.Hidden;
    m_popup.VerticalAlignment = System.Windows.VerticalAlignment.Top;
    m_popup.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
    
    mainGrid.Children.Add(m_popup);

    esto alinea el popup a la izquierda y arriba, de modo que sera la referencia para posicionarlo.

    Luego a la hora de mostrar el popup, se debe de posicionar comprobando los limites, algo como esto:

    // Si existe seleccion, hacer visible para modificar la selecciony y pasarsela
    if (richTextBox1.Selection.IsEmpty == false)
    {
      // obtener posicion del raton
      Point pointer = Mouse.GetPosition(mainGrid);
      // obtener rectangulo que rodea el ultimo caracter de la seleccion (para obtener sus dimensiones)
      Rect rectChar = richTextBox1.Selection.End.GetCharacterRect(System.Windows.Documents.LogicalDirection.Backward);
    
      double x, y;
      // evaluar posicion donde se mostrara (arriba o debajo de la seleccion)
      // por defecto se muestra arriba
      y = ((pointer.Y - m_popup.Height - rectChar.Height) > 0) ? 
        pointer.Y - m_popup.Height - rectChar.Height :
        pointer.Y + rectChar.Height;
      
      // evaluar posicion donde se mostrara (izquierda o derecha)
      // por defecto se muestra hacia la derecha
      x = ((pointer.X + m_popup.Width) < GetWindow(this).Width) ?
        pointer.X :
        pointer.X - m_popup.Width;
    
      // establecer el margen correcto al popup
      m_popup.Margin = new Thickness(x, y, 0, 0);
    
      // mostrar y establecer la seleccion
      m_popup.Visibility = System.Windows.Visibility.Visible;
      m_popup.Selection = richTextBox1.Selection;
    }

    En realidad se puede observar que la posicion del raton se obtiene facilmente, pero como supongo que se debera tener en cuenta el alto de la fuente (relativo a la seleccion), tambien lo he obtenido con la funcion 'GetCharacterRect()', con estas dos variables, se puede verificar donde sera mostrado el popup relativo a los limites de la ventana.

    Para calcular el ancho de la ventana, como no le he puesto nombre lo he realizado con la funcion 'GetWindow()' a modo de que sea conocida ya que puede solucionar algun apuro. Pero lo ideal es poner nombre a la ventana y obtener su propiedad directamente.

    Supongo que algo asi es lo que buscabas.

    Cuando tengo tiempo suelo escribir codigo para tener mi repositorio de snippets y de paso aprender sobre la tecnologia, asi se puede ayudar y aprender a la vez.

    Si tienes algun problema con el comentamelo.

     

     


    Saludos
    David González
    jueves, 29 de abril de 2010 12:05
  •  

    David, que tal

    muchas gracias por la ayuda

    Estuve analizando el codigo de posicionamiento, y note que este deberia estar en el Windows para ubicar el usercontrol, pero habria forma de hacerlo desde dentro del mismo popup.

    O sea recrear la funcionalidad que use la clase PopUp de WPF, en donde le indicas el control al cual se adjunta y relativo al mouse posicionar el control, pero la logica estar dentro del propio control, para que peuda ser reutilicable

    lo que no puedo encontrar es la forma en cambiar la localizacion del UserControl desde dentro del mismo

    saludos


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina
    jueves, 29 de abril de 2010 16:10

  • Voy a abrir un nuevo post porque este en realidad ya estaria bien contestado, la verdas te pasaste con el ejemplo.

     

    Buscando un poco en profundidad despues de tu ejemplo puede ver que habia implementaciones similares, aplicando esta tecnica

    WPF Popup control. (Part 2, User controls as Popups)

     

    saludos


    Leandro Tuttini

    Blog
    Buenos Aires
    Argentina
    jueves, 29 de abril de 2010 20:27
  • Si, en el ejemplo heredan de control, con lo que se puede personalizar aun mas, pero es bastante mas complejo definir toda la funcionalidad en el control que en un UserControl. 

    Tiene cosas interesantes el articulo que has puesto.

     

    Saludos
    David González
    viernes, 30 de abril de 2010 6:19