Mostrando entradas con la etiqueta WPF. Mostrar todas las entradas
Mostrando entradas con la etiqueta WPF. Mostrar todas las entradas

jueves, 1 de octubre de 2015

WPF (Windows Presentantion Fundation)

CONTROLES

Varios Apuntes WPF


Fuente: Tutorial de WPF

miércoles, 26 de agosto de 2015

Estilo para un TextBlock cuando tiene el focus

<!-- Estilo para cambiar el cursos del textblock cuando tiene el foco -->
        <Style TargetType="TextBlock" x:Key="FlatStyleButton">
            <Setter Property="Foreground" Value="#000000"/>
            <Setter Property="Background" Value="#FFFFFF" />
            <Style.Triggers>
                <Trigger Property="IsMouseOver" Value="True">
                    <Setter Property="Cursor" Value="Hand" />
                </Trigger>
                <EventTrigger RoutedEvent="UIElement.MouseEnter">
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" Storyboard.TargetProperty="Foreground.Color" To="CornflowerBlue" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
                <EventTrigger RoutedEvent="UIElement.MouseLeave">
                    <BeginStoryboard>
                        <Storyboard>
                            <ColorAnimation Duration="0:0:0.3" Storyboard.TargetProperty="Foreground.Color" To="Black" />
                        </Storyboard>
                    </BeginStoryboard>
                </EventTrigger>
            </Style.Triggers>
        </Style>

viernes, 21 de marzo de 2014

Setear error en las filas de un grid Devexpress

Para validar la información de un registro y si no pasa dicha validación setear el icono del error en la fila y columna del registro, hacemos algo como lo estamos haciendo en el siguiente ejemplo:

* La clase que estamos utilizando como fuente de datos, la heredamos de IDXDataErrorInfo

public class Person : object, IDXDataErrorInfo {
        public Person(string firstName, string lastName, string address, string phoneNumber, string email) {
            this.FirstName = firstName;
            this.LastName = lastName;
            this.Address = address;
            this.PhoneNumber = phoneNumber;
            this.Email = email;
        }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Address { get; set; }
        public string PhoneNumber { get; set; }
        public string Email { get; set; }

        #region IDXDataErrorInfo Members
        void IDXDataErrorInfo.GetPropertyError(string propertyName, ErrorInfo info) {
            switch(propertyName) {
                case "FirstName":
                case "LastName":
                    if(IsStringEmpty(propertyName == "FirstName" ? FirstName : LastName)) {
                        SetErrorInfo(info, propertyName + " field can't be empty", ErrorType.Critical);
                    }
                    break;
                case "Address":
                    if(IsStringEmpty(Address)) {
                        SetErrorInfo(info, "Address hasn't been entered", ErrorType.Information);
                    }
                    break;
                case "Email":
                    if(IsStringEmpty(Email)) {
                        SetErrorInfo(info, "Email hasn't been entered", ErrorType.Information);
                    } else if(Email != "none" && !IsEmailCorrect(Email)) {
                        SetErrorInfo(info, "Wrong email address", ErrorType.Warning);
                    }
                    break;
            }
        }
        void IDXDataErrorInfo.GetError(ErrorInfo info) {
            if(IsStringEmpty(PhoneNumber) && (Email == "none" || !IsEmailCorrect(Email)))
                SetErrorInfo(info, "Either Phone Number or Email should be specified", ErrorType.Information);
        }
        #endregion
        bool IsStringEmpty(string str) {
            return str == null || str.Trim().Length == 0;
        }
        bool IsEmailCorrect(string email) {
            return email == null || (email.IndexOf("@") >= 1 && email.Length > email.IndexOf("@") + 1);
        }
        void SetErrorInfo(ErrorInfo info, string errorText, ErrorType errorType) {
            info.ErrorText = errorText;
            info.ErrorType = errorType;
        }

domingo, 2 de febrero de 2014

Controles WPF

  • ComboBox 
Este control, muestra una fuente de datos en un componente desplegable tipo ItemsControl. Para tener un correcto funcionamiento cuando la fuente de datos, por ejemplo una entidad, se debe tener en cuenta lo siguiente:
    • ItemsSource = puede ser un Binding a una clase.
    • DisplayMemberPath="Description" 
    • SelectedValuePath="Code"
    • SelectedValue = un Binding a una propiedad de la clase.
 Cuando necesitamos crear un ComboBox de multiple selección con Devexpress:
<dxe:ComboBoxEdit x:Name="checkedComboBox" ItemsSource="{Binding FiltersMV.Shifts}" ShowSizeGrip="False" IsTextEditable="False" ShowCustomItems="True" MinWidth="100">
    <dxe:ComboBoxEdit.StyleSettings>
        <dxe:CheckedComboBoxStyleSettings />
    </dxe:ComboBoxEdit.StyleSettings>
</dxe:ComboBoxEdit>


  • TimeSpan
Para trabajar con horas y validar si una hora esta en un rango de horas de varias fechas, podemos utilizar este control y realizar dichas validaciones:

//Fecha actual
DateTime currentDate = Servinte.Clinic.Core.Reads.ProxyHelper.CurrentDate();               
////Horario actual
TimeSpan CurrentShift = new TimeSpan(currentDate.Hour, currentDate.Minute, currentDate.Second);
//Configuracion del turno de la Mañana
TimeSpan ShiftMornig = new TimeSpan(Convert.ToInt32(VariablesKardex.VariablesConfiguration.NursingShiftStartTimeMorning.Value.ConfigValue.Substring(0, 2)), Convert.ToInt32(VariablesKardex.VariablesConfiguration.NursingShiftStartTimeMorning.Value.ConfigValue.Substring(3, 2)), 0);
//Configuracion del turno de la tarde
TimeSpan ShiftAfternoon = new TimeSpan(Convert.ToInt32(VariablesKardex.VariablesConfiguration.NursingShiftStartTimeAfternoon.Value.ConfigValue.Substring(0, 2)), Convert.ToInt32(VariablesKardex.VariablesConfiguration.NursingShiftStartTimeMorning.Value.ConfigValue.Substring(3, 2)), 0);
//Configuracion del turno de la noche
TimeSpan ShiftNight = new TimeSpan(Convert.ToInt32(VariablesKardex.VariablesConfiguration.NursingShiftStartTimeNight.Value.ConfigValue.Substring(0, 2)), Convert.ToInt32(VariablesKardex.VariablesConfiguration.NursingShiftStartTimeMorning.Value.ConfigValue.Substring(3, 2)), 0);

//Turno que seleccionaremos por defecto
ShiftDefaul = TimeSpan.Compare(CurrentShift, ShiftMornig) >= 0 && TimeSpan.Compare(CurrentShift, ShiftAfternoon) < 0 ? ShiftDefaul = FiltersMV.Shifts.Where(w => w.Code.Equals("MA")).FirstOrDefault() :
    TimeSpan.Compare(CurrentShift, ShiftAfternoon) >= 0 && TimeSpan.Compare(CurrentShift, ShiftNight) < 0 ? FiltersMV.Shifts.Where(w => w.Code.Equals("TA")).FirstOrDefault() :
    ShiftDefaul = FiltersMV.Shifts.Where(w => w.Code.Equals("NO")).FirstOrDefault();

  •  Comando de MVVM para un evento
Para bindear un comando de una ViewModel, a un evento de un control de la vista, realizamos lo siguiente:

<ListBox Name="employeeListBox" ItemsSource="{Binding empList}" Grid.Row="0" SelectedItem="{Binding SelectedIndex}">
  <i:Interaction.Triggers>
    <i:EventTrigger EventName="SelectionChanged">
      <i:InvokeCommandAction Command="{Binding MyCommand}" CommandParameter="{Binding ElementName=employeeListBox, Path=SelectedValue}"/>
    </i:EventTrigger>
  </i:Interaction.Triggers>
</ListBox>


  •  Binding de una propiedad MVVM a un DataTemplate
Para bindear una propiedad de la ViewModel a un DataTemplate, realizamos lo siguiente:
<DataTemplate>
    <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.ErrorMessageDate }"/>
</DataTemplate>


  •  Realizar bloqueo en tabla
Para guardar información en la base de datos y requerimos primero bloquear la tabla, realizamos lo siguiente:
try
{
   transaction = database.BeginTransaction();               
   if(!database.LockTable(transaction, "HHACTPRO", "ACTPROSUO", string.Format(" actprosuo = {0} and actprospo = {1}", param["1"], param["2"], param["3"]), true))
         throw new ApplicationException("La reprogramación del registro esta siendo modificada por otro usuario. En el momento no se puede procesar esta acción");
                
   int detailResult = database.ExecuteNonQuery(sql, transaction, parameters);
   transaction.Commit();

}
catch(Exception ex)
{
    transaction.Rollback();
    throw;
 }


  •  Obtener la secuencia de una tabla
Para consulta la última secuencia de una tabla para realizar un insert, realizamos lo siguiente:

//Consultamos la siguiente secuencia del registro que vamos a insertar, para eso utilizamos la funcion GetAutonumeric(). ESto solo para ORACLE
if (BaseRuntime.ProviderName.Equals("Oracle"))
{
    sequence = db.GetAutonumeric("sq_hfrevord_sec", transaction);
    param.Add(sequence, DbType.Int64);                                       
}

//Consultamos la última secuencia del registro insertado en la tabla, para esto utilizamos la función. Esto para cualquier motor menos ORACLE
if (!BaseRuntime.ProviderName.Equals("Oracle"))
{
    sequence =  db.GetLastSerial("hfrevord", transaction);                                       
    param.Add(sequence, DbType.Int64);
}


  •  Generar un evento de doble clic en un control con MVVM
Para generar un evento de doble clic en un control lo realizamos con el siguiente código:

//Agregamos el namespace que necesitaremos en el trigger, y el DataContext es la VM
xmlns:spc="clr-namespace:Servinte.Presentation.Core;assembly=Servinte.Presentation.Core"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
 
<Grid>
<i:Interaction.Triggers>
    <spc:DoubleClickTrigger>
        <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}, Path=DataContext.ConsultOrderInformationCommand}" CommandParameter="{Binding OrderTreatment}"/>
    </spc:DoubleClickTrigger>
</i:Interaction.Triggers>
</Grid>


  • Mascara para un textedit devexpress
Para construir un control que solo permita ingresar números, con un determinado número de decimales, utilizamos la mascara. Este es el código de ejemplo:

//Agregamos la mascara con #,###,### para indicar que máximo puede ingresar 7 enteros y .## dos decimales
 
<dxe:TextEdit Name="txtPrice" Text="{Binding AmountRegister}" MinHeight="30" MaskType="Numeric" MaskUseAsDisplayFormat="True" Mask="#,###,###.##" HorizontalContentAlignment="Right" />

  • Validacion en un control devexpress
Para realizar una validación en un control de devexpress, realizamos lo siguiente:

//Agregamos la propiedad Validate y el método que va a realizar la validación de los datos. Las otras propiedades de validación es para permitir dejar el control con el error y validar al momento de digitar los datos.
 
<dxe:TextEdit Name="txtAmountRegister" Text="{Binding AmountRegister}" Validate="txtAmountRegister_Validate" InvalidValueBehavior="AllowLeaveEditor" ValidateOnTextInput="False" IsEnabled="{Binding IsEnableRegister, Converter={StaticResource boolToVisibilityConverter},ConverterParameter=AmountRegister}" MinHeight="30" MaskType="Numeric" MaskUseAsDisplayFormat="True" Mask="###,###.0" HorizontalContentAlignment="Right" MaskAutoComplete="Optimistic">

  • Line de separador
Para pintar una linea de separador en la interface de WPF, se realiza lo siguiente:

//Debemos asegurarnos que el namespace x, ya este invocado en el archivo con el siguiente código: 
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Luego agregamos la siguiente línea en el código XAML:
 
<Separator Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />

  • Rotar texto en un control
Para rotar el texto de un control, podemos realizar lo siguiente:

<TabControl TabStripPlacement="Left">
    <TabItem Header="Tab 1">
        <TabItem.LayoutTransform>
            <RotateTransform Angle="-90"></RotateTransform>    
        </TabItem.LayoutTransform>
        <TextBlock> Some Text for tab 1</TextBlock>
    </TabItem>
    <TabItem Header="Tab 2">
        <TabItem.LayoutTransform>
            <RotateTransform Angle="-90"></RotateTransform>
        </TabItem.LayoutTransform>
        <TextBlock> Some Text for tab 2</TextBlock>
    </TabItem>
</TabControl>


  • Bindear el evento load de un control
Para bindear el evento load de un control, a un método del ViewModel, realizamos lo siguiente:

//Debemos asegurarnos que el namespace x, ya este invocado en el archivo con el siguiente código: 
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
Luego agregamos el código XAML en el control:
 
<i:Interaction.Triggers>
    <i:EventTrigger EventName="Loaded">
        <i:InvokeCommandAction Command="{Binding MyICommandThatShouldHandleLoaded}" />
    </i:EventTrigger>
</i:Interaction.Triggers>


//En la viewModel debemos crear un ICommand, para enlazarlo a este.

  • Creación de tramas
Para crear tramas y pintarlas en un control, por ejemplo como background, realizamos lo siguiente:

//Primero debemos crear como recurso la trama de VisualBrush, para luego llamarla desde la propiedad del componente.
 
<VisualBrush x:Key="DiagonalLeftLines" TileMode="Tile" Viewport="0,0,20,1" ViewportUnits="Absolute" Viewbox="0,0,10,10" ViewboxUnits="Absolute">
    <VisualBrush.RelativeTransform>
        <RotateTransform Angle="-45" CenterY="0.5" CenterX="0.5"/>
    </VisualBrush.RelativeTransform>
    <VisualBrush.Visual>
        <Canvas>
            <Path Stroke="Red" Data="M 4 0 l 0 10" StrokeThickness="1"  />
        </Canvas>
    </VisualBrush.Visual>
</VisualBrush>
<VisualBrush x:Key="DiagonalRightLines" TileMode="Tile" Viewport="0,0,20,1" ViewportUnits="Absolute" Viewbox="0,0,10,10" ViewboxUnits="Absolute">
    <VisualBrush.RelativeTransform>
        <RotateTransform Angle="45" CenterY="0.5" CenterX="0.5"/>
    </VisualBrush.RelativeTransform>
    <VisualBrush.Visual>
        <Canvas >
            <Path Stroke="Red" Data="M 4 0 l 0 10" StrokeThickness="1"  />
        </Canvas>
    </VisualBrush.Visual>
</VisualBrush>


//En la interface gráfica, hacemos el enlace desde la propiedad al recurso de la trama: 
<TextBlock Text="{Binding}" Background="{StaticResource DiagonalLeftLines}" TextTrimming="CharacterEllipsis" TextWrapping="Wrap"/>

Interface INotifyPropertyChanged

  • Framework 4.5      
 public event PropertyChangedEventHandler PropertyChanged;

        private void SetProperty<T>(ref T field, T value, [CallerMemberName] string name = "")
        {
            if (!EqualityComparer<T>.Default.Equals(field, value))
            {
                field = value;
                var handler = PropertyChanged;
                if (handler != null)
                {
                    handler(this, new PropertyChangedEventArgs(name));
                }
            }
        }

  • Framework < 4.5
public event PropertyChangedEventHandler PropertyChanged;

private void RaisePropertyChanged(string propertyName)
        {
            // take a copy to prevent thread issues
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(propertyName));
            }
        }

miércoles, 6 de marzo de 2013

Asignar botones a notas del historico

Cada nota del histórico debe llevar ciertas configuraciones, por ejemplo, para asignar botones a una nota del histórico, seguimos los siguientes pasos:

  1. En la entidad Messages-ClinicDocumentEntity, creamos los comandos para manejar los métodos que van a disparar el hacer clic en el botón. Estos comandos los creamos como propiedades.
  2. En la misma entidad creamos unas boleanas de visibilidad, para saber si se estarán disponibles los botones o no. Estas boleanas las creamos como propiedades, las cuales van a disparar el evento de cambio de valor, tanto para la boleana sola, como la de la boleanda de mostrar comando.
  3. Se crea en el DynamicTemplateHelper dependiendo del código del comando, configuramos estos en el texto estructurado.
  4. En el componente del histórico de notas, se agregan al bindeo de los comandos el respectivo comando y creamos su respectivo método que va hacer lo que le corresponde.

miércoles, 27 de febrero de 2013

Ventana Popup En Control WPF

Para asignar una ventana popup a un control, se puede realizar la siguiente lógica. En nuestro ejemplo vamos a mostrar una ventana popup para un boton, osea al dar clic en el botón se mostrara  la ventana.
El código para mostrar la ventana se agrega en el evento clic del botón:


private void Boton_Click(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
        {
            try
            {
                Dictionary<string, string> paramUserControl = new Dictionary<string, string>();
                paramUserControl.Add("unParametro");

                //Instanciamos el control wpf que va a mostrar la información.
                UCExample u = new UCExample ();
                u.SetContext(paramStateTreatment);

                //Instanciamos la ventana contenedora del control
                Popup popup = new Popup()
                {
                    Child = u,
                    AllowsTransparency = true,
                    StaysOpen = true,
                    IsOpen = false,
                    PlacementTarget = ((System.Windows.Controls.Control)(e.OriginalSource)),
                    Placement = PlacementMode.Left
                };

                //Binding para setear la propiedad "Width" del contenedor con la ventana de la información
                Binding widthBinding = new Binding("Width") { Mode = BindingMode.OneWay, Source = popup };
                ((FrameworkElement)u).SetBinding(WidthProperty, widthBinding);

                 //Le damos un tiempo para mostrar la ventana
                DispatcherTimer activeTimer = new DispatcherTimer { Interval = new TimeSpan(0,0,7) };
                activeTimer.Tick += (obj, ea) => ClosePopup(popup);
                activeTimer.Start();

                popup.IsOpen = true;
                return;
            }
            catch (Exception)
            {
                throw;
            }
        }

Dentro de una ventana popup puede ir cualquier template. El código del usercontrol que va a ir dentro de la ventana popup, es el siguiente:

XAML

<UserControl x:Class="Servinte.Clinic.Controls.UCSeeTreatmentStatus"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d" Height="Auto" Width="Auto">
    <UserControl.Resources>
        <Style TargetType="Label" x:Key="lblControl">
            <Setter Property="HorizontalContentAlignment" Value="Left"/>
        </Style>
        <Style TargetType="TextBlock" x:Key="txtbControl">
            <Setter Property="HorizontalAlignment" Value="Left"/>
            <Setter Property="VerticalAlignment" Value="Center"/>
        </Style>
    </UserControl.Resources>
    <Border Background="White" BorderBrush="Black" BorderThickness="2">
        <Grid Margin="5">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/>
                <ColumnDefinition Width="Auto"/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
                <RowDefinition Height="Auto"/>
            </Grid.RowDefinitions>
            <Label Content="Nombre " Style="{StaticResource lblControl}"/>
            <TextBlock Grid.Column="1" Name="txtbName" Style="{StaticResource txtbControl}"/>
            <Label Grid.Row="1" Content="Apellidos " Style="{StaticResource lblControl}"/>
            <TextBlock Grid.Row="1" Grid.Column="1" Name="txtbLastName" Style="{StaticResource txtbControl}"/>
            <Label Grid.Row="2" Content="Profesión " Style="{StaticResource lblControl}"/>
            <TextBlock Grid.Row="2" Grid.Column="1" Name="txtbProfession" Style="{StaticResource txtbControl}"/>
            <Label Grid.Row="3" Content="Fecha Nacimiento " Style="{StaticResource lblControl}"/>
            <TextBlock Grid.Row="3" Grid.Column="1" Name="txtbDateBorn" Style="{StaticResource txtbControl}"/>
        </Grid>
    </Border>
</UserControl>


C#

using System.Collections.Generic;
using System.Windows.Controls;

namespace MyNamespace.Controls
{
    /// <summary>
    /// Interaction logic for UCExample.xaml
    /// </summary>
    public partial class UCExample : UserControl
    {
        public UCExample()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Seteamos el contexto de la ventana que informa los datos de la persona
        /// </summary>
        public void SetContext(Dictionary<string,string> param)
        {           
            //Nombre
            txtbName.Text = string.Format(": {0}", param["Name"]);
            //Apellidos
            txtbLastName.Text = string.Format(": {0}", param["LastName"]);
            //Profesion
            txtbProfession.Text = string.Format(": {0}", param["Profesion"]);
            //Fecha de nacimiento
            txtbDateBorn.Text = string.Format(": {0}",param["DateBorn"]));           
        }
    }
}



lunes, 3 de diciembre de 2012

Llamar una ventana Winform desde WPF

Hoy tuve un problema durante el proceso de desarrollo de un sistema en WPF y WinForms, sucede que el proyecto está hecho en WPF y eventualmente se utilizaron WinForms para su manejo pero la interoperabilidad no viene habilitada por defecto, es decir, al instanciar una clase de tipo WinForm desde el WPF y mostrarla con el método Show() no tengo la posibilidad de cambiar entre controles dentro del WinForm con la tecla TAB, lo que al mostrarlo con el método ShowDialog() me funciona perfectamente; ahora bien, hay una solución bastante sencilla pero que me ha costado unas cuantas horas de investigación y publico aquí la solución:

  • Agregar a las referencias del proyecto la libreria WindowsFormsIntegration, generalmente localizada en %ProgramFiles%\Reference Assemblies\Microsoft\Framework\v3.0\WindowsFormsIntegration.dll


  • Importar la librería dentro del proyecto WPF de la siguiente manera: Imports System.Windows.Forms.Integration


  • Después de llamar al WinForm con el método .Show(), habilitar la interoperabilidad de la siguiente manera:System.Windows.Forms.Integration.WindowsFormsHost.EnableWindowsFormsInterop()

Eso es todo, espero que a alguien le sea útil.

Nota. Si es un proyecto WinForm y se agrega un WPF y se quiere la interoperabilidad, lo unico que cambia es la manera de habilitarla, se hace de la siguiente forma: System.Windows.Forms.Integration.ElementHost.EnableModelessKeyboardInterop()