June 2009 - Artículos

Shapes en WPF

Cuando necesitamos dibujar  contenido gráfico  en dos dimensiones  (2D)  con
WPF,  la  mejor  forma  es  usando  Shapes.  Realmente  los  Shapes  son  clases
dedicadas que se utilizan para representar simples  líneas, elipses, rectángulos
y polígonos varios. Los Shapes son conocidos como primitivos porque son los
precursores de gráficos más complejos mediante la combinación de diferentes
tipos de shapes.
WPF  provee  seis  clases  que  derivan  de  la  clase  abstracta
System.Windows.Shapes.Shape:

Rectangle


De  los  Shapes  más  sencillos  con  el  que  crearemos  diferentes  tipos  de
rectángulos,  simplemente  son necesarios  establecer  las propiedades Width  y
Height.

<Rectangle Fill="Yellow" Stroke="Blue" Height="50" Width="100" Margin="5" HorizontalAlignment="Left"/> 
image 

Mediante  las propiedades RadiusX y RadiusY podemos  conseguir  rectángulos
con bordes redondeados.

image 

 

 

 

Ellipse


Al igual que el shape Rentangle, el shape Ellipse es utilizado para crear elipses
fácilmente. Estableciendo las propiedades Width y Height tenemos una elipse.

 

<Ellipse Fill="Yellow" Stroke="Blue" Height="50" Width="100" Margin="5" HorizontalAlignment="Left"/> 
image 
Line


Representa la línea que une dos puntos. Por tanto tenemos las coordenadas X
e Y de ambos puntos.

<Line Stroke="Blue" X1="120" Y1="50" X2="280" Y2="125"></Line> 

image

 

Polyline


Nos  permite  dibujar  una  secuencia  de  líneas  rectas  conectadas.  Para  ello
debemos  especificar  una  lista  de  coordenadas  X  e  Y  que  representan  los
puntos por los que pasará la recta.

<Polyline Stroke="Red" StrokeThickness="5" Points="10,150 30,140 50,160 70,130 190,100 210,240" /> 
image 
Polygon


Podríamos decir que este shape es muy parecido al anterior, al Polyline, ya que
recibe  una  lista  de  puntos  que  componen  el  polígono,  con  la  diferencia
fundamental que se unen el punto de inicio con el último punto.

<Polygon Stroke="Red" StrokeThickness="5" Points="10,150 30,140 50,160 70,130 190,100 210,240" />  

image

Path


El shape Path puede ser usado para representar formas complejas que incluyan
curvas y arcos. Para poder usar, debemos usar un tipo especial de sintaxis en
los atributos para asignar la propiedad Data.
En el siguiente ejemplo vemos algo de lo que es capaz de aportarnos el shape
Path:

 

 

 

<Path Fill="LightSkyBlue" Stroke="Black"> 
   <Path.Data> 
     <CombinedGeometry GeometryCombineMode="Union"> 
       <CombinedGeometry.Geometry1> 
          <EllipseGeometry Center="100,150" RadiusX="70" RadiusY="30" /> 
       </CombinedGeometry.Geometry1> 
       <CombinedGeometry.Geometry2> 
          <EllipseGeometry Center="200,150" RadiusX="70" RadiusY="30" /> 
       </CombinedGeometry.Geometry2> 
     </CombinedGeometry> 
   </Path.Data> 
</Path> 

 

image

Si recordais el post de Panel vemos que la  clase  Shape  deriva  de  la  clase
FrameworkElement, por lo que podemos decir que los Shapes son elementos.

Gracias a esto, tenemos las siguientes ventajas:

  • Autodibujado :El  Shape  se  autogestion  en  este  sentido  y  no  debemos  preocuparnos  por
    redibujar el shape cuando alguna propiedad del mismo cambia, ya sea tamaño,
    posición, etc.
  • Organización similar a otros elementos :Es  decir,  podemos  utilizar  los  shapes  como  elementos,  por  tanto  no  hay
    problema de incluirlos en cualquier contenedor de los descritos en anteriores
    capítulos.
  • Tienen Eventos :Disponemos de los mismos eventos que otros elementos, es decir, no debemos
    preocuparnos  en  el  trabajo  extra  que  supondría  añadir  la  posibilidad  de
    controlar eventos de ratón o similares, ya los tenemos implementados.
Publicado por Oskar Alvarez | 6 comment(s)
Archivado en:

Otro ejemplo de aplicación realizada en WPF

Aquí tenéis un video de una aplicación realizada en WPF Cool Movies

 

Publicado por Oskar Alvarez | 1 comment(s)
Archivado en:

Custom Layouts en WPF

Hemos estado viendo en los anteriores post los diferentes layouts que viene en WPF, pero también podremos crear nuestro porpio contendor y que funcione como nosotros diseñemos. Esto es una gran flexibilidad a la hora de diseñar nuestra aplicación, para ello deberemos crear una clase que derive de Panel que explique en este post. Si recordamos los conceptos mas importantes son:

Measure

En estado de medición  (Measure) el contenedor comprueba por cada uno de
los  elementos  hijos  su  tamaño  deseado,  es  decir,  les  “pregunta”  que  tamaño
pretende ocupar en el contenedor.

Arrange

En este estado de distribución (Arrange), el contenedor coloca a cada uno de
los elementos hijos en su posición apropiada.

Ya que deberemos sobreescribir los metodos MeasureOverride y ArrangeOverride para programar como se comporta nuestro control.

Un ejemplo muy sencillo es

public class MySimplePanel : Panel
    {
        // Make the panel as big as the biggest element
        protected override Size MeasureOverride(Size availableSize)
        {
            Size maxSize = new Size();

            foreach (UIElement child in InternalChildern)
            {
                maxSize.Height = Math.Max(child.DesiredSize.Height, maxSize.Height);
                maxSize.Width = Math.Max(child.DesiredSize.Width, maxSize.Width);
            }
        }

        // Arrange the child elements to their final position
        protected override Size ArrangeOverride(Size finalSize)
        {
            foreach (UIElement child in InternalChildern)
            {
                child.Arrange(new Rect(finalSize));
            }
        }
    }

Podéis probar lo que hace.

 

Uno mas complejo es el que he llamado AnimatedWrapPanel, se comporta como un WraPanel pero cuando pasas el raton por encima de u elemento se realiza una animación para que se vea mas grande y cuando sale el ratón vuelve a su tamaño

 

 /// <summary>
    /// Wrap Panel que crea una animacion cuando se construyen,
    /// los items van desde la posicion 0 hasta su lugar
    /// </summary>
    public class AnimatedWrapPanel : Panel
    {
        private TimeSpan animationLength =
            TimeSpan.FromMilliseconds(500);
        private TimeSpan animationSelected =
          TimeSpan.FromMilliseconds(200);
        private double scaleselected = 1.2;
        private Size sizecontrol = new Size();
        private bool animating = false;
        public AnimatedWrapPanel()
        {
            this.MouseEnter += new MouseEventHandler(AnimatedWrapPanel_MouseEnter);
            this.MouseLeave += new MouseEventHandler(AnimatedWrapPanel_MouseLeave);
            this.MouseMove += new MouseEventHandler(AnimatedWrapPanel_MouseMove);
        }

        void AnimatedWrapPanel_MouseMove(object sender, MouseEventArgs e)
        {
            if (!animating)
                AnimateSelected();
        }

        private void AnimateSelected()
        {
            if (this.Children == null || this.Children.Count == 0)
                return;
            animating = true;

            if (this.IsMouseOver)
            {
                double x = Mouse.GetPosition(this).X;
                foreach (FrameworkElement child in this.Children)
                {
                    if (child.IsMouseOver)
                    {

                        if (child.RenderTransform is TranslateTransform)
                        {
                            TranslateTransform trans = child.RenderTransform as TranslateTransform;
                            TransformGroup transgroup = new TransformGroup();
                            transgroup.Children.Add(trans);
                            ScaleTransform scale = new ScaleTransform();
                            scale.CenterX = trans.X;
                            scale.CenterY = trans.Y;
                            transgroup.Children.Add(scale);
                            child.RenderTransform = transgroup;
                            DoubleAnimation da = MakeAnimation(scaleselected);


                            scale.BeginAnimation(ScaleTransform.ScaleXProperty, da, HandoffBehavior.SnapshotAndReplace);

                            scale.BeginAnimation(ScaleTransform.ScaleYProperty,
                           da,
                            HandoffBehavior.SnapshotAndReplace);



                        }
                        else if (child.RenderTransform is TransformGroup)
                        {
                            TransformGroup transgroup = child.RenderTransform as TransformGroup;
                            ScaleTransform scale = transgroup.Children[1] as ScaleTransform;
                            TranslateTransform trans = transgroup.Children[0] as TranslateTransform;

                            DoubleAnimation da = MakeAnimation(scaleselected);


                            scale.BeginAnimation(ScaleTransform.ScaleXProperty,
                         da,
                            HandoffBehavior.Compose);
                            scale.BeginAnimation(ScaleTransform.ScaleYProperty,
                            da,
                            HandoffBehavior.Compose);
                        }
                    }
                    else if (child.RenderTransform is TransformGroup)
                    {
                        TransformGroup transgroup = child.RenderTransform as TransformGroup;
                        ScaleTransform scale = transgroup.Children[1] as ScaleTransform;
                        TranslateTransform trans = transgroup.Children[0] as TranslateTransform;

                        if (scale.ScaleX == 1.2)
                        {

                            scale.BeginAnimation(ScaleTransform.ScaleXProperty,
                            new DoubleAnimation(1, animationSelected),
                            HandoffBehavior.Compose);
                            scale.BeginAnimation(ScaleTransform.ScaleYProperty,
                            new DoubleAnimation(1, animationSelected),
                            HandoffBehavior.Compose);

                        }

                    }

                }
            }

            animation_Completed(null, null);
        }

        private DoubleAnimation MakeAnimation(double to)
        {
            DoubleAnimation da = new DoubleAnimation(to, animationSelected);
            da.AccelerationRatio = 0.8;
            da.DecelerationRatio = 0.2;
            return da;
        }
        void animation_Completed(object sender, EventArgs e)
        {
            animating = false;
        }
        void AnimatedWrapPanel_MouseLeave(object sender, MouseEventArgs e)
        {
            AnimateSelected();
        }

        void AnimatedWrapPanel_MouseEnter(object sender, MouseEventArgs e)
        {
            AnimateSelected();
        }
        protected override Size MeasureOverride(Size availableSize)
        {
            Size infiniteSize = new Size(double.PositiveInfinity,
                double.PositiveInfinity);
            double curX = 0, curY = 0, curLineHeight = 0;
            foreach (UIElement child in Children)
            {
                child.Measure(infiniteSize);

                if (curX + child.DesiredSize.Width > availableSize.Width)
                { //Siguiente lineaa
                    curY += curLineHeight;
                    curX = 0;
                    curLineHeight = 0;
                }

                curX += child.DesiredSize.Width;
                if (child.DesiredSize.Height > curLineHeight)
                    curLineHeight = child.DesiredSize.Height;
            }

            curY += curLineHeight;

            Size resultSize = new Size();
            resultSize.Width = double.IsPositiveInfinity(
                availableSize.Width) ? curX : availableSize.Width;
            resultSize.Height = double.IsPositiveInfinity(
                availableSize.Height) ? curY : availableSize.Height;

            return resultSize;
        }

        protected override Size ArrangeOverride(Size finalSize)
        {
            if (this.Children == null || this.Children.Count == 0)
                return finalSize;

            TranslateTransform trans = null;
            double curX = 0, curY = 0, curLineHeight = 0;
            int zindexposition = Children.Count;
            foreach (UIElement child in Children)
            {
                child.SetValue(ZIndexProperty, zindexposition);
                zindexposition -= 1;
                trans = child.RenderTransform as TranslateTransform;

                if (trans == null)
                {
                    child.RenderTransformOrigin = new Point(0, 0);
                    trans = new TranslateTransform();
                    child.RenderTransform = trans;
                }

                if (curX + child.DesiredSize.Width > finalSize.Width)
                { //Siguiente Linea
                    curY += curLineHeight;
                    curX = 0;
                    curLineHeight = 0;
                }

                child.Arrange(new Rect(0, 0, child.DesiredSize.Width,
                    child.DesiredSize.Height));

                trans.BeginAnimation(TranslateTransform.XProperty,
                    new DoubleAnimation(curX, animationLength),
                    HandoffBehavior.Compose);

                trans.BeginAnimation(TranslateTransform.YProperty,
                    new DoubleAnimation(curY, animationLength),
                    HandoffBehavior.Compose);

                curX += child.DesiredSize.Width;
                if (child.DesiredSize.Height > curLineHeight)
                    curLineHeight = child.DesiredSize.Height;
            }
            sizecontrol = finalSize;
            return finalSize;
        }
        public void Animate()
        {
            TranslateTransform trans = null;
            double curX = 0, curY = 0, curLineHeight = 0;

            foreach (UIElement child in Children)
            {
                child.RenderTransform = null;
                trans = child.RenderTransform as TranslateTransform;

                if (trans == null)
                {
                    child.RenderTransformOrigin = new Point(0, 0);
                    trans = new TranslateTransform();
                    child.RenderTransform = trans;
                }

                if (curX + child.DesiredSize.Width > sizecontrol.Width)
                { //Siguiente Linea
                    curY += curLineHeight;
                    curX = 0;
                    curLineHeight = 0;
                }

                child.Arrange(new Rect(0, 0, child.DesiredSize.Width,
                    child.DesiredSize.Height));

                trans.BeginAnimation(TranslateTransform.XProperty,
                    new DoubleAnimation(curX, animationLength),
                    HandoffBehavior.Compose);

                trans.BeginAnimation(TranslateTransform.YProperty,
                    new DoubleAnimation(curY, animationLength),
                    HandoffBehavior.Compose);

                curX += child.DesiredSize.Width;
                if (child.DesiredSize.Height > curLineHeight)
                    curLineHeight = child.DesiredSize.Height;
            }
            //this.InvalidateArrange();
        }

    }

El resultado es

 

image

Publicado por Oskar Alvarez | 1 comment(s)
Archivado en:

ViewBox en WPF

Un elemento Viewbox es un contenedor que escala todos sus elementos secundarios de un modo similar a un control de zoom, solo puede tener un hijo y no una colección como teníamos en los anteriores contenedores, realmente se dice que es un contenedor de diseño.

Se encuentra en el assembly System.Windows.Controls, realmente solo escala para ajustar el contenido al tamaño, no hace un resize sino que realiza una transformación (que veremos mas adelante). Esto significa que todos los objetos tienen el mismo comportamiento que si ponemos en la propiedad  Stretch de un path o image el valor Uniform.

Yo lo utilizo para independizarme de la resolución del monitor, siempre diseño para la resolución estándar del proyecto pero meto un viewbox para que si se ejecuta en una resolución diferente sea mayor o menor  la aplicación se ajuste automáticamente, tiene muchas mas utilizadas un viewbx, debeis de experimentar con el.

 

<Window x:Class="WpfViewBox.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="300" Width="300">
    <Viewbox Stretch="Fill">
        <Canvas Height="480" Width="720" Background="Gray" Margin="10">
            <Ellipse Height="300" Width="500" Fill="Yellow" 
                     Canvas.Top="100" Canvas.Left="100" />
        </Canvas>

    </Viewbox>
</Window>

Este codigo produce como resultado:

 

image

 

Fijaros en las propiedades Height y Width y como la elipse se adapta a un tamaño menor. Si damos a maximizar la ventana

 

image

 

Vemos que la elipse se hace mas achatada en mi equipo es porque tengo una pantalla paronímica y al valor que le he puesto en stretch, jugar con los valores de strecth y veréis las importantes diferencias que hay entre un valor y otro.

Los valores que puede tener la propiedad Stretch son

  • Fill
  • None
  • Uniform
  • Uniform
  • UniformToFill

A jugar….

Publicado por Oskar Alvarez | 1 comment(s)
Archivado en:

Discusiones de WPF 090626

Del blog de Jaime Rodiguez se han publicado emails de discusiones internas del equipo de Microsoft, aquí las tenéis, son bastante interesantes

 

 


Subject: Advise for running WPF apps over Citrix

Answer:
All versions of WPF since WPF 3.5 SP1 have remoted (both with Remote Desktop and Terminal Server) using Bitmap Remoting.
Bitmap remoting works as follows:

  • The application is rendered on the server using WPF’s software rasterizer
  • As the application runs, the server keeps track of which regions of the application’s window are newly dirty and need to be updated
  • When a region needs to be updated, the server creates a compressed bitmap of just the dirty region and sends that to the client
  • Once the client has drawn that bitmap to the appropriate place on its own window, the client window is up-to-date

Given how this remoting mechanism works, performance can be maximized in several ways:

  • Dirty regions should be kept as small as possible so the least amount of data is sent over the wire
  • Ambient animations should be turned off
    • For instance, setting a window background to an animating gradient would cause the entire window to be invalidated / redrawn every frame
  • The system does not optimize away occluded parts of the application
    • For instance, an animation that is completely hidden behind some other opaque element will still cause dirty region invalidation / bitmap generation to occur.  Remove these from your application.
  • Dirty regions should be created as infrequently as possible
    • Turn off as many animations as possible
    • For those animations that can’t be eliminated completely, lower the animation framerate using the DesiredFramerate property
  • Dirty Region Bitmaps should be as simple as possible to maximize their compression
    • Application running over TS should favor solid colors over gradients or other exotic fills (unnecessary images, etc), especially for application pieces that will be redrawn frequently
  • Avoid operations that are especially slow when rendered in software
    • BitmapEffects / Effects / ShaderEffects, especially blurs and drop shadows with large radii, are quite slow in software
    • 3D – the 3D software rasterizer is substantially slower than rendering in hardware

Subject: Is there any support in WPF for “bottom-up” bitmaps?
I’m using BitmapSource.Create with an unmanaged memory buffer. The buffer comes from a Win32 DIBSection with a +ve height, which indicates a bottom-up DIB.

Answer:
Wrap it in a TransformedBitmap. 
return new TransformedBitmap(bmp, new ScaleTransform(1,-1));

[extra tip]
Note that I think each call to CopyPixels() on the TransformedBitmap will run the scale transformation again. You can avoid this by caching the result in a CachedBitmap and using the CB instead


Subject: Getting DependencyProperty value from WinDBG
I have a dump of a WPF process and am trying to get the value of a DependencyProperty of UIElement.  Does anybody know how this can be done?

Answer:
1. Pull out the value of _packedData.
2. _packedData & 0x0000FFFF will give you the GlobalIndex.
3. Go to the DependencyObject on which you want to query the property and get access to the _effectiveValues array.
4. Go through the EffectiveValueEntry objects, looking at the _propertyIndex field until it matches the index you calculated in step 2 (the array should be sorted by property index).
5. _value is the value of the property on that DependencyObject.


Subject:  Difference between nulll and x:Null for brushes

I know there is a difference but I can’t remember. I think it had something to do with hit testing (but I can’t remember for sure).

Answer:
No difference.  You're probably thinking of null (No hit test) versus Transparent (Can be hit).


Subject: Disable auto-word selection in FlowDocumentViewer
How can I make FlowDocumentViewer select character by character when I use the mouse?

Answer:
For FlowDocumentPageViewer, you can use flowDocumentPageViewer.SetValue(TextBoxBase.AutoWordSelectionProperty, false). FlowDocumentScrollViewer doesn’t auto-select words by default.

Internally, the selection code shared by text boxes and FlowDocument viewers relies on TextBoxBase.AutoWordSelectionProperty. We don’t currently expose the property on anything other than TextBoxBase.


Subject: Blend units.
Any reason why Blend3 outputs font size in WPF “units” and not the actual pt value. If I change the FontSize on a TextBlock to “11pt”, 14.667 is written out.
Answer:
Blend 2 always displayed text units in pixels. This was confusing to most designers who are used to thinking of fonts in terms of points. In Blend 3 we added an option where you can choose whether you want points or pixels as your display in the Property panel. This is under the Units tab in the Options dialog.

In terms of the XAML output, we had wanted to output the same units as shown in the UI but Silverlight does not support the pt syntax in XAML so we decided (for cost and consistency reasons) to just leave it as pixels in the XAML for V3.  It is something for us to consider improving in the future though.


Subject: Disable IME on TextBox
Is there a setting to disable using IME on a textbox, or some other means of not allowing a user to use it?

Answer:
<TextBox InputMethod.IsInputMethodEnabled=”false” />


Subject: DateTime property in the Properties editor of Blend
I have a question regarding how Blend handles a control property which is of type System.DateTime.  I am trying to understand how the property editor would interpret a string such as 20/05/2001. Would it take into account the regional settings of the computer or is it tied up with the implementation of the control itself?

Answer:
Blend will use a text editor to read and display DateTimes, and will parse input based on the locale settings of your machine.  In xaml, it is always stored in an invariant format(year-month-day).


Subject:  (summarized) issue w/ WPF not matching GDI on how a custom font is rendered

Answer:
There is no expectation that WPF font rasterization will match GDI\GDI+ rasterization. For example if the font does not have italic or bold variants GDI will emulate bold and italic WPF will not.


Subject: DPI interop issues...

I have a drag/drop gesture that draws a drag/drop feedback in a transparent WPF window. The problem is in tracking the mouse position I had to use interop to Win32.GetCursorPos and that works, when the operating system is in 96 DPI mode.  But when you change the OS DPI to something else, like 120 or 150 DPI then this doesn’t work because positioning the transparent WPF drag/drop feedback window requires WPF device independent coordinates (essentially 96 DPI) so I need to do a conversion


Answer:
Have you looked at Visual.PointFromScreen?  It can be used to convert screen coordinates from APIs such as GetCursorPos into Device Independent Units


Subject: Game loops in WPF?
wondering how easy it would be to write a custom "game loop" within the WPF framework. By game loop, I mean pumping my own events on a custom Dispatcher so I can control the framerate of my app.

Answer:
You can write your own message pump. Just don't call Dispatcher.Run. You need to pump messages, and you need to call ComponentDispatcher.RaiseThreadMessage as appropriate. Use reflector to see what Dispatcher.PushFrame does.

Of course, this still may not give you what you want for a "game loop"...


Subject: Is there a way to prevent exception if DataContext is null?

This is just an example.

I have a Image which is Databind to some dynamic source (image source) and it works fine and it is all done in xaml. Can we provide some defaule image source incase DataContext becomes null?

Answer:

FallbackValue is your friend: http://msdn.microsoft.com/en-us/library/system.windows.data.bindingbase.fallbackvalue.aspx


Subject: Listening to selective ETW Events

I’m writing performance tests where we want to listen to the WClientUceNotifyPresent ETW event to calculate frame rates.  However, I haven’t found provider flags to listen to it, along with a limited set of other events.

Answer:

With WPF 3.0-3.5 you unfortunately don’t have a lot of options.  You should make sure that you’re using verbosity level 4 (as opposed to 5).

If you’re on CLR 4.0 we have much more granular flags so you’ll be able to turn on a flag specifically for Graphics related events which should help .  If you have a recent drop (post Beta1) you should find our event manifest installed in \windows\microsoft.net\framework\v4.0\wpf\wpf-etw.man which lists the available flags.  You will need either flag 1 `General` or flag 0x1000 `Graphics` depending on what drop you have.


Subject: Way to check if an UIElement is visible in ScrollViewer

Is there any way to check if an UI element is already showing in a scroll viewer?

Answer:
[Not right one, but works]
Use reflection to invoke ScrollViewer.IsInViewport method?

[Final]
I wouldn’t recommend this for production code.
Is there a reason you don’t want to call BringIntoView on elements that are already visible? All handlers of RequestBringIntoView I know of no-op on visible elements.

Publicado por Oskar Alvarez | 2 comment(s)

WPF Toolkit June

Se ha liberado en codeplex el WPF Toolkit, en esta versión se han corregido importantes bugs y los controles que se incluyen son:

  • DataGrid
  • DatePicker
  • Calendar
  • VisualStateManager

La novedad es que también incluye, en calidad “preview”, un nuevo control de chart –prácticamente el mismo que el de Silverlight Toolkit.

 

wpf_chartpreview

Mayor información (y descarga) en:

Publicado por Oskar Alvarez | 2 comment(s)
Archivado en:

El Grid de WPF

Por  nivel de  complejidad  es  el más  complejo  pero  a  la  vez  es  el  contenedor
más  versátil. Mucho  de  lo  que  podemos  conseguir  con  el  resto  de  paneles
contenedores lo podemos crear con el Grid.
Para  realizar  un  uso  básico  del  Grid,  en  primer  lugar  debemos  definir  el
número  de  columnas  y  filas  que  tendrá.  Esto  lo  conseguiremos  añadiendo
tantos RowDefinition y ColumnDefinitions por  fila y columna que queramos
en las propiedades RowDefinitions y ColumnDefinitions.
Una  vez  difinidas  las  filas  y  columnas,  para  posicionar  cada  elemento  en  su
respectiva  celda  utilizaremos  las  Attached  Properties  del  contenedor  Row  y
Column, pudiendo aparecer más de un elemento por celda lógicamente.
En el siguiente código XAML y su resultado podemos ver un ejemplo de Grid:

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1"> 
    <Grid Name="mainPanel"> 
         
        <!-- Definición de 3 Columas --> 
        <Grid.ColumnDefinitions> 
            <ColumnDefinition></ColumnDefinition> 
            <ColumnDefinition></ColumnDefinition> 
            <ColumnDefinition></ColumnDefinition> 
        </Grid.ColumnDefinitions> 
         
        <!-- Definición de 2 Filas--> 
        <Grid.RowDefinitions> 
            <RowDefinition></RowDefinition> 
            <RowDefinition></RowDefinition> 
        </Grid.RowDefinitions> 
         
        <Label Content="Label 1 (Col=0 ;Row=0)" Grid.Column="0" Grid.Row="0" Background="Red" /> 
        <Label Content="Label 2 (Col=1 ;Row=0)" Grid.Column="1" Grid.Row="0" Background="GreenYellow" /> 
        <Label Content="Label 3 (Col=2 ;Row=0)" Grid.Column="2" Grid.Row="0" Background="Yellow" /> 
        <Label Content="Label 4 (Col=0 ;Row=1)" Grid.Column="0" Grid.Row="1" Background="Blue" /> 
        <Label Content="Label 5 (Col=2 ;Row=1)" Grid.Column="2" Grid.Row="1" Background="Orange" />
     </Grid> 
</Window> 
 

El resultado seria:

 

image

 

Si  queremos más  complejidad,  tenemos  la  posibilidad  de  expandir  tanto  las
columnas como  las  filas utilizando otras dos Attached Properties: RowSpan y
ColumnSpan.

Como  nota  final  podemos  destacar  la  posibilidad  que  nos  brindan  las
propiedades Height y Width de la definición de las columnas y filas. El tipo de
dato de estas propiedades no es double sino System.Windows.GridLength, por
lo que pueden tomar diferentes tipos de valores, a saber:

  • Tamaños Absolutos :Corresponde  a un  valor numérico que  representa pixeles  independientes del
    dispositivo.
  • Automático :Asignando el valor Auto,  asigna a los elementos hijos el espacio que necesitan
    única y exclusivamente, y no más.
  • Proporcional  :Asigna a cada elemento el espacio disponible dividido de forma proporcional.
Publicado por Oskar Alvarez | 1 comment(s)
Archivado en:

Silverlight + PlayBoy bonito tratamiento de imágenes

Bondi Digital Publishing, empresa que se dedica a digitalizar revistas , ha digitalizado 53 números emblemáticos de la revista, cubriendo el tiempo entre Enero de 1954 y Mayo de 2006

El sitio y servicio Playboy Archive fue diseñado y desarrollado en Silverlight. Cada revista ha sido digitalizada completamente y cuenta con un sistema de búsqueda en vivo que permite encontrar palabras y contenidos interactivamente.

 

Esta muy bien (y es agradable) echarle un vistazo y ver la tecnología DeepZoom de Silverlight

 

Publicado por Oskar Alvarez | 1 comment(s)

Material de la charla de WPF

El día 6 de Mayo di en  Artalde la charla 10 razones para elegir WPF, en la que di un repaso general de WPF

Aquí tenéis la ppt

Publicado por Oskar Alvarez | 2 comment(s)
Archivado en:

Creación de un Property Grid en WPF Simple y Rapido

Me voy a saltar un poco el tutorial para enseñaros un control, tampoco es muy complejo creo que se puede seguir perfectamente

En una de mis recientes aplicaciones he utilizado el control Property Grid de Denis Vuyka ya que tuve que realizar un diseñador de formularios en WPF, pero ese sera un tema para mas adelante.

Me he planteado realizar uno, no tan profesional como el de Denis, aquí tenéis el planteamiento, la idea es dividir el control en tres partes

image

La búsqueda realizara un filtrado de las propiedades, luego tendremos un StackPanel que contendra el nombre de la propiedad y el valor y por ultimo la descripción de la propiedad seleccionada.

En el control tendremos una propiedad SelectedObject de tipo Object a la cual le pasaremos la instancia del objeto del cual queremos mostrar sus propiedades, una vez pasado un objeto a esta propiedad rellenaremos la lista de propiedades que sera un StackPanel a la que iremos añadiendo items por cada propiedad del objeto

Aquí tenéis el código para sacar la lsita de propiedades

 

                this.PropertyPanel.Children.Clear(); //Limpiar la lista de propiedades 
                
                foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(value))
                {
                    if (!property.IsBrowsable) continue; 
                    PropertyItem currentProperty = new PropertyItem();
                    currentProperty.PropertyName = property.Name;        
                    Binding b = new Binding(property.Name);
                    b.Source = selectedObject;
                    b.Mode = property.IsReadOnly ? BindingMode.OneWay : BindingMode.TwoWay;
                   
                    currentProperty.SetBinding(PropertyItem.PropertyValueProperty, b);
                    currentProperty.OnActive += new EventHandler<DescriptionEventArgs>(currentProperty_OnActive);
                    
                    foreach (Attribute attribute in property.Attributes)
                    {
                        if (attribute.GetType() == typeof(DescriptionAttribute))
                        {
                            currentProperty.PropertyDescription = ((DescriptionAttribute)attribute).Description;
                        }
                       if (attribute.GetType() == typeof(CategoryAttribute)) {
                            currentProperty.PropertyCategory = ((CategoryAttribute)attribute).Category;
                        }
                    }      
                    PropertyPanel.Children.Add(currentProperty); 
                }

El Xaml del control es tan sencillo como el que sigue

 

<HeaderedContentControl x:Class="PropertyGridDemo.PropertyGrid"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:pg="clr-namespace:PropertyGridDemo" Height="146" Width="189">
    <HeaderedContentControl.Resources>
        <ResourceDictionary Source="Themes/Generic.xaml"/>
    </HeaderedContentControl.Resources>
    <HeaderedContentControl.Header>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="50"/>
                <ColumnDefinition Width="*"/>
            </Grid.ColumnDefinitions>
            <TextBlock Text="Buscar:" Grid.Column="0" VerticalAlignment="Center" HorizontalAlignment="Center"/>
            <TextBox x:Name="searchTextBox" Grid.Column="1"/>
        </Grid>
    </HeaderedContentControl.Header>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition Height="50"/>
        </Grid.RowDefinitions>
        <ScrollViewer x:Name="propertyGridScrollBar" Grid.Row="0" CanContentScroll="False" VerticalScrollBarVisibility="Visible">
            <ScrollViewer.Content>
                <StackPanel x:Name="PropertyPanel"/> 
        </ScrollViewer.Content>
        </ScrollViewer>
        <TextBlock x:Name="descriptionTextBlock" Grid.Row="1" TextWrapping="Wrap"/>
    </Grid>
</HeaderedContentControl>

El resultado es el siguiente pasándole a la propiedad la instancia de un Button

image 

Os dejo a vosotros terminarlo y dejarlo bonito bonito

 

Espero que os hay gustado

 
Publicado por Oskar Alvarez | 1 comment(s)
Archivado en:

Tamaño mínimo y máximo de los contenedores en WPF

Julio me pregunta en el post sobre StackPanel y DockPanel :

“¿Existe algunha manera de "declarativamente" decirle a WPF que existe un tamaño mínimo para estos contenedores?

Lo pregunto pq a veces cuando estas redimensionando una ventana interesa que haya un "tope mínimo" donde el usuario no pueda hacerla más pequeña”

Todos los contenedores tienen dos tipos de propiedades MinWidth – MinHeight y MaxWidth – MaxHeight donde le posemos decir el mínimo y el máximo que puede coger el contenedor.

En este pantallazo del Blend tenemos el área de color rojo que es un WrapPanel

 

image

En el área de propiedades podéis ver que tiene las propiedades MinWidth – MinHeight 

En XAML

<WrapPanel Margin="28,37,34,59" Background="#FFFA0707" MinWidth="100" MinHeight="200"/>

Lo mismo pasa con las propiedades MaxWidth – MaxHeight

 

image

<WrapPanel Margin="28,37,34,59" Background="#FFFA0707" MinWidth="100" MinHeight="200" MaxWidth="400" MaxHeight="600"/>

Espero que haya respondido a Julio y a todos los que tengan esta duda

 

Saludos Oskar

Publicado por Oskar Alvarez | 1 comment(s)
Archivado en:

Wrap Panel de WPF

El  panel  WrapPanel  es  muy  parecido  al  StackPanel  ya  que  organiza  los
elementos  hijos  de  forma  similar,  a  saber,  de  derecha  a  izquierda  o  de
izquierda  a  derecha,  sin  embargo  la  diferencia  fundamental  es  que  este
contenedor  añade  filas  o  columnas dependiendo del  espacio disponible para
los elementos hijos. 
Es  utilizado  habitualmente  cuando  no  conocemos  el  número  exacto  de
elementos hijos que contendrá el panel.
Como  ejemplo  podemos  ver  el  siguiente  código  XAML  donde  en  un
WrapPanel añadimos seis controles de tipo Label identificados y de diferentes
colores.

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1"> 
    <WrapPanel Name="mainPanel"> 
        <Label Content="Label 1" Background="Red" Width="75"/> 
        <Label Content="Label 2" Background="GreenYellow" Width="75"/> 
        <Label Content="Label 3" Background="Yellow" Width="75"/> 
        <Label Content="Label 4" Background="AliceBlue" Width="75"/> 
        <Label Content="Label 5" Background="Orange" Width="75"/> 
        <Label Content="Label 6" Background="White" Width="75"/> 
    </WrapPanel> 
</Window> 

El resultado de la ejecución:

image

Publicado por Oskar Alvarez | 1 comment(s)
Archivado en:

Concurso de Silverlight con 10.000$ de premio

De la mano de Tim Heuer me entero que hay una competición de Silverlight del 22 de Junio hasta el 22 de Septiembre con un premio de 10.000$ organizada por ComponentArts

Aquí tenéis la entrada donde lo explica : http://timheuer.com/blog/archive/2009/06/25/win-10-thousand-dollars-silverlight-coding-competition.aspx

A programar este verano

 

Saludos Oskar

Publicado por Oskar Alvarez | 1 comment(s)
Archivado en: ,

Diferencias de programación entre Silverlight y WPF

Wintellect acaba de poner en Codeplex un white paper sobre las diferencias entre WPF y Silverlight totalmente recomendable

programming differences between WPF and Silverlight”. 

 

Necesario si queréis que vuestra aplicación funciones tanto en WPF como en SilverLight

Publicado por Oskar Alvarez | 1 comment(s)
Archivado en:

Guía de WCF para desarrolladores de WPF

En el maravilloso sitio de Codeplex se acaba de publicar la guía de WCF para desarrolladores de WPF, en ella podéis encontrar ejemplos, webcast de como plantear los servicios de WCF en aplicaciones WPF, van desde la introducción hasta problemas como manejo de excepciones, multithreading, manejo de la vida del proxy…

 

Podéis acceder a ella desde  aquí, os la recomiendo.

Publicado por Oskar Alvarez | 2 comment(s)
Archivado en: ,

Stack Panel y DockPanel WPF


El siguiente tipo de Contenedor que sigue al Canvas en grado de complejidas
es el StackPanel. Es uno de los mas usados debido a su facilidad de uso gracias
a su simplicidad.
Este panel ordena los elementos hijos de forma secuencial, es decir de derecha
a izquierda o de arriba a abajo. Para ver un ejemplo sencillo podemos utilizar

el  código  fuente  del  ejemplo  mostrado  en  el  panel  Canvas  y  modificar  el
contenedor  por  un  StackPanel.  Hemos  eliminado  las  propiedades  adjuntas
donde se especificaba la posición realtiva de cada Textbox y añadido un color
de  fondo  para  una  mejor  visualización.  El  código  XAML  quedaría  de  la
siguiente manera:

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1"> 
    <StackPanel Name="mainCanvas" Background="Black"> 
        <TextBox Text="1" Background="Red"/> 
        <TextBox Text="2" Background="GreenYellow" /> 
        <TextBox Text="3" Background="Yellow"/> 
        <TextBox Text="4" Background="AliceBlue"/> 
        <TextBox Text="5" Background="Orange"/> 
        <TextBox Text="6" Background="White"/> 
    </StackPanel> 
</Window> 

Siendo el resultado por defecto el siguiente:

image 

Pudiendo  cambiar  la orientación  de  los  objetos  hijos mediante  la  propiedad

Orientation.

 

image

DockPanel

Definido por las etiquetas <DockPanel></DockPanel>, que es el reemplazo de la propiedad Dock de los controles de versiones anteriores, ahora en WPF, se utilizan paneles para definir las áreas de específicas del formulario. Es rígido con los controles, al colocarlos no es simple manipularlos en su interior, por lo que se recomienda incluir un Canvas o un Grid por comodidad, la importancia de este control es para ubicar las áreas del formulario, las cuales se definen estableciendo las propiedades VerticalAlignment para definir el Top, Center, Bottom o Stretch, y la propiedad HorizontalAlignment para definir el Left, Right, Center, Stretch. Cabe mencionar que al utilizar Stretch en ambas propiedades da un resultado parecido a Fill, también es prudente mencionar que se debe utilizar la propiedad Margin para ubicar bien los paneles en sus límites con otros paneles.

 

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1"> 
    <DockPanel Name="mainPanel"> 
        <Label Content="Label 1 (Top)" DockPanel.Dock="Top" Background="Red" /> 
        <Label Content="Label 2 (Left)" DockPanel.Dock="Left" Background="GreenYellow" /> 
        <Label Content="Label 3 (Right)" DockPanel.Dock="Right" Background="Yellow" /> 
        <Label Content="Label 4 (Bottom" DockPanel.Dock="Bottom" Background="AliceBlue" /> 
        <Label Content="Label 5" Background="Orange" /> 
    </DockPanel> 
</Window> 

image

Publicado por Oskar Alvarez | 2 comment(s)
Archivado en:

Browser con ejemplos de WPF

 

Mike Taulty de Microsoft UK tiene una aplicación distribuida por Click Once, en la cual tiene ejemplos de WPF y de controles de tercero. Se necesita el .NET Framework V3.5 Sp1para su instalación

image

 

Para instalar la aplicación pincha directamente aqui y se instalara a través de Click once.

 

Espero que os guste

 

Publicado por Oskar Alvarez | 2 comment(s)
Archivado en:

Manual / Tutorial de Microsoft Expression Blend

Etiquetas de Technorati:

Siempre que programes en WPF debes de saber utilizar su herramienta de diseño, si he dicho diseño y cuando lo digo en un circulo de programadores todos se echan a temblar y las típicas frases “yo soy programador y el diseño no es lo mio”, “no tengo gusto” … Pero señores Expression Blend es una herramienta también para el programador, una cosa es el diseño de la IU y otra es ponerse a ello, retocar… por eso los programadores no debemos tenerla miedo, a mi por lo menos me parece una herramienta sencilla y muy útil.

Para aprender tenéis aquí un tutorial en castellano de Expression Blend 2.0 muy completo

 

screenshot_sm

Publicado por Oskar Alvarez | 3 comment(s)
Archivado en:

Nuestro Primer contenedor de WPF - Canvas

Canvas

Es el panel más simple, sencillo y flexible en el Presentation Framework. Este
tipo  de  panel  posiciona  cada  elemento  hijo  en  posiciones  relativas  con
coordenadas explícitas.
Para posicionar elementos dentro de un Canvas necesitamos  las propiedades
Left  y Top, Right  y Bottom, Left  y Bottom o Right  y Top que posicionarán  el

elemento  con  respecto  al  borde  que  corresponda  del  Canvas  contenedor.
Además  para  establecer  el  orden  de  superposición  de  los  elementos  en  el
Canvas existe la propiedad zIndex.
Si  vemos  un  ejemplo,  en  el  siguiente  código  XAML  se  muestra  un  Canvas
como  Panel  contenedor  con  seis  controles  de  tipo  TextBox  colocados
utilizando las propiedades Left, Top, Right y Bottom.

 

<Window x:Class="WpfApplication1.Window1" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Window1"> 
    <Canvas Name="mainCanvas" Background="Black"> 
        <TextBox Text="Left=0 y Top=0"/> 
        <TextBox Text="Left=15 y Top=15" Canvas.Left="15" Canvas.Top="15"/> 
        <TextBox Text="Right=15 y Bottom=15" Canvas.Right="15" Canvas.Bottom="15"/> 
        <TextBox Text="Right=0 y Bottom=0" Canvas.Right="0" Canvas.Bottom="0"/> 
        <TextBox Text="Right=0 y Top=0" Canvas.Right="0" Canvas.Top="0"/> 
        <TextBox Text="Left=0 y Bottom=0" Canvas.Left="0" Canvas.Bottom="0"/> 
    </Canvas> 
</Window> 

Y el resultado es el siguiente:

image

 
Publicado por Oskar Alvarez | con no comments
Archivado en:

Controles de WPF Gratis

Telerik tiene una oferta especial para los que tengan una subscripción de MSDN y es dar una licencia de desarrollo gratis valorada en 799$. Podeis echarla un vistazo accediendo a http://www.telerik.com/products/wpf.aspx

La oferta es valida solo hasta el 31 de Julio así que aun tenéis tiempo.

Para bajar el control ir a vuestra pagina de login de MSDN , click en la pestaña “Special Offer” y elegir “Exclusive deal by Telerik: RadControls for WPF – FREE Developer License“.

 

Publicado por Oskar Alvarez | 2 comment(s)
Más artículos Página siguiente >