Krisztián's profileBátyai Krisztián[KRis]PhotosBlogListsMore Tools Help

Blog


    September 12

    Rolling Menu

     

    menu

    Találtam egy érdekes/designos menüt, amit egy kicsit átalakítottam, hogy bármilyen UI elemet tudjon fogadni, és azt egyszerűen meg lehessen adni, és design alatt látni hogy mit gyártunk...

    Átalakítás:

    <UserControl x:Class="SilverKatalogus.MenuImage"
    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"
        d:DesignWidth="90" d:DesignHeight="90"
        >
        <UserControl.Resources>
            <Storyboard x:Name="Storyboard1">
                <DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="content" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
                    <SplineDoubleKeyFrame KeyTime="00:00:00.2000000" x:Name="MyKeyFrame" Value="-1">
                        <SplineDoubleKeyFrame.KeySpline>
                            <KeySpline ControlPoint1="0,0" ControlPoint2="0.504000008106232,1"/>
                        </SplineDoubleKeyFrame.KeySpline>
                    </SplineDoubleKeyFrame>
                </DoubleAnimationUsingKeyFrames>
            </Storyboard>
        </UserControl.Resources>
        <!-- don't remove the background, required for correct LeaveEvent-->
        <Grid x:Name="LayoutRoot" 
                MouseEnter="Image_MouseEnter" 
                MouseLeave="image_MouseLeave"
                Background="Transparent"
                Cursor="Hand"
                RenderTransformOrigin="0.5,0.5">
            <ContentPresenter RenderTransformOrigin="0.5,0.5" x:Name="content">
                <ContentPresenter.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform />
                    </TransformGroup>
                </ContentPresenter.RenderTransform>
            </ContentPresenter>
            <!--<Image 
                   RenderTransformOrigin="0.5,0.5" x:Name="image">
                <Image.RenderTransform>
                    <TransformGroup>
                        <ScaleTransform />
                    </TransformGroup>
                </Image.RenderTransform>
            </Image>-->
            <Grid.RenderTransform>
                <TransformGroup>
                    <RotateTransform x:Name="MyAngle"/>
                </TransformGroup>
            </Grid.RenderTransform>
        </Grid>
    </UserControl>
    

     

    Látható, hogy egy kép helyett ContentPresenterben jelenítem meg a dolgokat...

    Ehhez 2 DependencyProperty-re van szükség, és ezt a UIElement-et jelenítem meg a 2 kép helyett (normal, rolled).
    Így gyakorlatilag bármit meg tudunk jeleníteni!

    (a kódban a dep propertyk a lényegek, csak a teljesség kedvéért tettem ide az egész osztályt...)

    public partial class MenuImage : UserControl
       {
           public MenuImage()
           {
               InitializeComponent();
    
               Storyboard1.Completed += new EventHandler(Storyboard1_Completed);
    
           }
    
           private enum MyStates { Normal, Started1, Started2, Reverse1, Reverse2 };
           private MyStates MyState = MyStates.Normal;
    
    
           public int Duration
           {
               get { return (int)GetValue(DurationProperty); }
               set { SetValue(DurationProperty, value);MyKeyFrame.KeyTime = KeyTime.FromTimeSpan(new TimeSpan(0, 0, 0, 0, value));  }
           }
    
           // Using a DependencyProperty as the backing store for Duration.  This enables animation, styling, binding, etc...
           public static readonly DependencyProperty DurationProperty =
               DependencyProperty.Register("Duration", typeof(int), typeof(MenuImage), null );
    
    
    
           public int Angle
           {
               get { return (int)GetValue(AngleProperty); }
               set { SetValue(AngleProperty, value);      MyAngle.Angle = value;}
           }
    
           // Using a DependencyProperty as the backing store for Angle.  This enables animation, styling, binding, etc...
           public static readonly DependencyProperty AngleProperty =
               DependencyProperty.Register("Angle", typeof(int), typeof(MenuImage), null);
    
    
    
           public UIElement UINormal
           {
               get { return (UIElement)GetValue(UINormalProperty); }
               set { SetValue(UINormalProperty, value); content.Content = value; }
           }
    
           // Using a DependencyProperty as the backing store for UINormal.  This enables animation, styling, binding, etc...
           public static readonly DependencyProperty UINormalProperty =
               DependencyProperty.Register("UINormal", typeof(UIElement), typeof(MenuImage), null);
    
    
           public UIElement UIRoll
           {
               get { return (UIElement)GetValue(UIRollProperty); }
               set { SetValue(UIRollProperty, value); }
           }
    
           // Using a DependencyProperty as the backing store for UIRoll.  This enables animation, styling, binding, etc...
           public static readonly DependencyProperty UIRollProperty =
               DependencyProperty.Register("UIRoll", typeof(UIElement), typeof(MenuImage), null);
    
    
    
           private void Reverse2() {
               MyState = MyStates.Reverse2;
               //this.image.Source = _normalImage;
               content.Content = UINormal;
    
               MyKeyFrame.Value = 1;
               Storyboard1.Begin();
           }
    
           private void Reverse1() {
               MyState = MyStates.Reverse1;
               MyKeyFrame.Value = 0;
               Storyboard1.Begin();
           }
    
           private void Start1() {
               MyState = MyStates.Started1;
               MyKeyFrame.Value = 0;
               Storyboard1.Begin();
           }
    
           private void Start2() {
               MyState = MyStates.Started2;
               //this.image.Source = _rollImage;
               content.Content = UIRoll;
               MyKeyFrame.Value = 1;
               Storyboard1.Begin();
           }
    
           private void Image_MouseEnter(object sender, MouseEventArgs e) {
               Canvas.SetZIndex(this, Canvas.GetZIndex(this) + 4);
               switch (MyState) {
                   case MyStates.Normal:
                   case MyStates.Reverse2:
                       Start1();
                       break;
                   default:
                       Start2();
                       break;
               }
           }
    
           private void image_MouseLeave(object sender, MouseEventArgs e) {
               Canvas.SetZIndex(this, Canvas.GetZIndex(this) - 4);
               switch (MyState) {
                   case MyStates.Started2:
                       Reverse1();
                       break;
                   case MyStates.Started1:
                       Reverse2();
                       break;
               }
           }
    
           void Storyboard1_Completed(object sender, EventArgs e) {
               switch (MyState) {
                   case MyStates.Started1:
                       Start2();
                       break;
                   case MyStates.Reverse1:
                       Reverse2();
                       break;
                   case MyStates.Reverse2:
                       MyState = MyStates.Normal;
                       break;
               }
           }
       }

    Használat:

    <local:MenuImage Angle="10" Width="120" Height="120" Margin="10">
                 <local:MenuImage.UINormal>
                     <Image Source="Images/users.png"></Image>
                 </local:MenuImage.UINormal>
                 <local:MenuImage.UIRoll>
                     <Border BorderBrush="Gray" BorderThickness="2" CornerRadius="3" >
                         <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10">Oktatók</TextBlock>
                     </Border>
                 </local:MenuImage.UIRoll>
             </local:MenuImage>

    És az eredmény :


    September 11

    ScreenCast

     

    Elkészült az első screencastom, első első és első Silverlight 2.0.

    Hogy is szokták mondani?!?
    Kicsit ...., kicsit...., de legalább az enyém :)

    Remélem hamarosan jön a többi is, egy értelmes tematika szerint...

    http://www.devportal.hu/groups/silverlight/blog/archive/2008/09/10/els-silverlight2-screencast.aspx

    WebMappa : http://files.devportal.hu/Demo/

    --
    KRis

    September 10

    XAML && Binding && Converter && IMultiValueConverter

     

    Igen hasznos dolog WPF/SL-os XAML újdonság a Binding. Ügyesen használva minimális "erőfeszítéssel", értsd kódolással-hekkeléssel, egész komplex működést is el lehet érni.

    Milyen problémákba ütközhetünk?

    • Source megadása/megtalálása (nem simán DataContext)
      (Property az egyszerű, mivel csak ki kell tölteni a Path-t)
    • Konverzió
    • Konverzió paraméterezése-> fix paraméter ( valamilyen paraméterre van szükség)
    • Konverzió paraméterezése-> több paraméter is szükséges, több helyről...

    Oldjuk fel a problémákat!!

    Source megadása

    • RelativeSource használata Binding-ban:
      {Binding RelativeSource={RelativeSource Self}}
    • Template esetén : {Binding RelativeSource={RelativeSource TemplatedParent}}
    • Ős megkeresése : {Binding RelativeSource={RelativeSource FindAncestor,
      AncestorLevel=n, AncestorType={x:Type desiredType}}}
    • Adott elem megkeresése : {Binding ElementName=elemName,.... }

    Ezzel gyakorlatilag mindent le lehet fedni, mindenkit meg lehet találni akinek a Property-jét fel akarjuk használni.

    Konverzió

    Erre a WPF-ben és SL-ban a konverterek állnak rendelkezésre:

    Gyári konverterek, pl:

    Ez sok esetben kevés, sőt :) általában kevés. De sebaj, gyártani tudunk sajátot:

    public class MyConverter : IValueConverter
       {
           #region IValueConverter Members
    
           public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
           {
               throw new NotImplementedException();
           }
    
           public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
           {
               throw new NotImplementedException();
           }
    
           #endregion
       }

    Mint látható kapunk egy értéket amit konvertálni kell, kapunk egy paramétert amit fel tudunk használni a konverzióhoz ( megkapjuk a cél type-t, és a culture-t).
    Implementáljuk a logikát és már is fel tudjuk használni a binding-ban:

    {Binding Converter="{StaticResource HeightConverter}"...}

    Paramétert a ConverterParameter="123"-al tudunk megadni a binding-ban.

    De.... mi van ha több paramétert akarok megadni, és bindolni szeretnék?!?

    Hát akkor nincs más hátra, mint az IMultiValueConverter:
    (a példából amit csatolok)

     

    public class HeightConverter : IMultiValueConverter
       {
    
           #region IMultiValueConverter Members
    
           public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
           {
               double value = (double)values[0];
               double height = (double)values[1];
               double min= (double)values[2];
               double max = (double)values[3];
    
               return (height-min) * (double)value / (max-min)*0.8;
           }
    
           public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
           {
               throw new NotImplementedException();
           }
    
           #endregion
       }

    Itt már nem csak egy értéket hanem bármennyit kaphatunk.... és a lényeg bárhonnan:

    <Rectangle x:Name="rectFill" Fill="DarkRed" HorizontalAlignment="Stretch" VerticalAlignment="Bottom">
             <Rectangle.Height>
                 <MultiBinding Converter="{StaticResource HeightConverter}"  >
                     <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Value"/>
                     <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="ActualHeight"/>
                     <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Minimum"/>
                     <Binding RelativeSource="{RelativeSource TemplatedParent}" Path="Maximum"/>
                 </MultiBinding>                                 
             </Rectangle.Height>
         </Rectangle>

    Mint látható minden egyes paraméternek külön meg lehet adni a forrást (jelen példában ugyanaz...), így akár több helyről is össze tudjuk szedni szükséges értékeket...

    Jelen példában készítettem a progressbar-t, ami stílusosan egy sörösüveg :), ami megtellik...
    ehhez szükség volt a progressbar value property-jére, és az aktuális magasságra...

    image

    Source : http://beta.devportal.hu/groups/wpf/media/p/946.aspx

    September 03

    Silverlight MDI Surface

     

    WPF-ben tanulási időszakban készítettem egy Surface-t amire mindenféle elemeket lehetett dobálgatni, ott átméretezni, rendezgetni stb.

    Ezt WPF-ben könnyen meg lehet tenni egy Canvas és a Thumb segítségével.

     

    De miért ne tennénk ezt meg Silverlight2-ben? És hívjuk az elemet Window-nak, legyen Title-je, és máris van egy Winforms MDI szerű rendszerünk!

    Lássuk!

    1. MDI Window

    Mi sem egyszerűbb! Mindösszesen egy Canvas:

    <UserControl x:Class="SilverWindowEngine.Page"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Width="800" Height="600">
        <Grid x:Name="LayoutRoot" Background="White">
            <Canvas x:Name="canvasMDI">
            </Canvas>
        </Grid>
    </UserControl>

    Természetesen, ha szeretnénk olyan funkciókat, hogy rendezés ( Cascade,Tile,stb), vagy Window listát, akkor azt itt implementálni kell.
    Ebben a demo-ban ezt most kihagyom.

    2. Szükségünk van egy Window control-ra, ami tud működni egy MDI felületen, azaz egy Canvas-on.

    Készítsünk egy UserControl-t : SilverWindow
    Mit kell tudnia?

    • Legyen címe
    • Legyen X, azaz bezár gomb
    • Legyen mozgatható
    • Legyen méretezhető
    • Ha a fejlécre (egyszerűség kedvéért) kattintok akkor jöjjön "előre"

    Ehhez alakítsuk ki a felületünket:
    Legyen a gridnek 2 sora 2 oszlopa

    <Grid.RowDefinitions>
               <
    RowDefinitionHeight="20"></RowDefinition>
               <
    RowDefinition></RowDefinition>
           </
    Grid.RowDefinitions>
           <
    Grid.ColumnDefinitions>
               <
    ColumnDefinition></ColumnDefinition>
               <
    ColumnDefinitionWidth="30"></ColumnDefinition>
           </
    Grid.ColumnDefinitions>       

    Cím:

    <StackPanel Grid.Row="0" Grid.Column="0"  HorizontalAlignment="Center">
               <TextBlock>Title</TextBlock>
           </StackPanel>     

    Mozgatás:

    <Rectanglex:Name="rectDrag"MouseLeftButtonDown="rectDrag_MouseLeftButtonDown"
    MouseMove="rectDrag_MouseMove" MouseLeftButtonUp="rectDrag_MouseLeftButtonUp"
    Fill="Transparent" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="0"  >
    </
    Rectangle>

    Bezárás:

    <Button x:Name="btnClose" Content="X" Grid.Column="1" Grid.Row="0" 
    Click="btnClose_Click"></Button>
    Tartalom:
    <Grid x:Name="grdContentPresenter" Grid.Row="1" Grid.ColumnSpan="2">
            </Grid>        

    Ebbe a gridbe fogjuk betenni az ablak tartalmát!

    Méretezés:

    <Rectangle x:Name="rectResizeWidth" 
    MouseLeftButtonDown="rectResizeWidth_MouseLeftButtonDown"
    MouseLeftButtonUp="rectResizeWidth_MouseLeftButtonUp"
    MouseMove="rectResizeWidth_MouseMove"
    Cursor="SizeWE" Grid.Column="1" Grid.RowSpan="2" Fill="Yellow"
    Width="7" Margin=" 0 0 -2 0" VerticalAlignment="Stretch"
    HorizontalAlignment="Right"></Rectangle> <Rectangle x:Name="rectResizeHeight"
    MouseLeftButtonDown="rectResizeHeight_MouseLeftButtonDown"
    MouseLeftButtonUp="rectResizeHeight_MouseLeftButtonUp"
    MouseMove="rectResizeHeight_MouseMove" Cursor="SizeNS"
    Grid.ColumnSpan="2" Grid.Row="1" Fill="Yellow" Height="7"
    Margin=" 0 0 0 -2" VerticalAlignment="Bottom" HorizontalAlignment="Stretch">
    </
    Rectangle>

    A megfelelő paraméterek beállításával a két szélén az ablaknak érzékeny lesz a 2 Rectangle, ha megfelelően írjuk meg az eseményvezérlőket, akkor pont azt fogják csinálni amit szeretnénk!
    Jelen esetben sárga ez a két terület, ezt át lehet állítani Transparent-re.
    (WPF-ben van egy Thumb osztály, ami megtalálható Silverlight-ban is, de sajnos sealed, így itt nem használható Drag-re. de sebaj néhány sor kód...)

    Díszítés:

    <Border BorderThickness="2" Grid.ColumnSpan="2" Grid.RowSpan="2" 
    BorderBrush="Black" CornerRadius="2"></Border>

    A sorrend azért fontos, hogy megfelelő legyen a fedése az egyes részeknek!!!

    Nézzzük meg az eseményvezérlőket:

    Mozgatás:

    #region Move
           public bool MouseDown { get; set; }
           public Point mouseDownPoint { get; set; }
           public Point mouseDownWindowPos { get; set; }
           private void rectDrag_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
           {
    
               Canvas c = this.Parent as Canvas;
               (this.Parent as Canvas).Children.Remove(this);
               c.Children.Add(this);
    
               double left = Canvas.GetLeft(this);
               double top = Canvas.GetTop(this);
    
               mouseDownPoint = e.GetPosition(this.Parent as Canvas);
    
               mouseDownWindowPos = new Point(left, top);
               MouseDown = true;
               rectDrag.CaptureMouse();
           }
    
           private void rectDrag_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
           {
               MouseDown = false;
               rectDrag.ReleaseMouseCapture();            
           }
    
           private void rectDrag_MouseMove(object sender, MouseEventArgs e)
           {
               if (!MouseDown)
               {
                   return;
               }
               double left = mouseDownWindowPos.X;
               double top = mouseDownWindowPos.Y;
    
               Point p = e.GetPosition(this.Parent as Canvas);
    
    
               double deltaHorizontal =  p.X - mouseDownPoint.X;
               double deltaVertical = p.Y -mouseDownPoint.Y;
    
    
               Canvas.SetLeft(this, left + deltaHorizontal);
               Canvas.SetTop(this, top + deltaVertical);
           }
    
           #endregion

    Vagyis egér lenyomásra megjegyzem a poziciót a Parent-hez képest (amiről tudom hogy Canvas), majd ezt felhasználom az egér mozgása eseményben. Ahol szépen beállítom a Canvas Left és Top property-jét, vagyis mozogni fog az ablak.

    Bezárás:

    private void btnClose_Click(object sender, RoutedEventArgs e)
        {
            (this.Parent as Canvas).Children.Remove(this);
        }

    Egyszerűen ki kell venni a Parent Canvas gyerekei közül...
    Itt lenne lehetőség olyan események kiváltására mint Closing... Closed... stb.

    Méretezés:

    #region SizeWidth
    
       public bool MouseDownSizeWidth { get; set; }
       public Point mouseDownPointSizeWidth { get; set; }
       public double mouseDownWidth { get; set; }
    
       private void rectResizeWidth_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
       {
    
           mouseDownPointSizeWidth = e.GetPosition(this.Parent as Canvas);
    
           MouseDownSizeWidth = true;
           mouseDownWidth = this.Width;
           rectResizeWidth.CaptureMouse();
    
       }
    
       private void rectResizeWidth_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
       {
           MouseDownSizeWidth = false;
           rectResizeWidth.ReleaseMouseCapture();     
       }
    
       private void rectResizeWidth_MouseMove(object sender, MouseEventArgs e)
       {
           if (!MouseDownSizeWidth)
           {
               return;
           }
           Point p = e.GetPosition(this.Parent as Canvas);
    
    
           double deltaHorizontal = p.X - mouseDownPointSizeWidth.X;
    
           this.Width = mouseDownWidth +deltaHorizontal;
       }
    
       #endregion
    
       #region SizeHeight
    
       public bool MouseDownSizeHeight { get; set; }
       public Point mouseDownPointSizeHeight { get; set; }
       public double mouseDownHeight { get; set; }
    
       private void rectResizeHeight_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
       {
           mouseDownPointSizeHeight = e.GetPosition(this.Parent as Canvas);
    
           MouseDownSizeHeight = true;
           mouseDownHeight = this.Height;
           rectResizeHeight.CaptureMouse();
       }
    
       private void rectResizeHeight_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
       {
           MouseDownSizeHeight = false;
           rectResizeHeight.ReleaseMouseCapture();     
       }
    
       private void rectResizeHeight_MouseMove(object sender, MouseEventArgs e)
       {
           if (!MouseDownSizeHeight)
           {
               return;
           }
           Point p = e.GetPosition(this.Parent as Canvas);
    
    
           double deltaVertical = p.Y - mouseDownPointSizeHeight.Y;
    
           this.Height = mouseDownHeight + deltaVertical;
       }
    
       #endregion

    Hasonlóan a mozgatáshoz, itt is figyeljük az egér mozgását.Csak most nem a Top és a Left propertyt állítjuk, hanem a Height és a Width property-ket!

    Már csak az maradt, hogy hozunk létre egy gyerekablakot?!?

    SilverWindow w1 = new SilverWindow(new DemoChildWindow());
               w1.Height = 200;
               w1.Width = 200;
    
               canvasMDI.Children.Add(w1);

    Erre akár készíthetünk egy általános CreateWindow statikus metódust is.
    (DemoChildWindow egy demo user control egy kevés tartalommal...)

    És voálá...

    image 

    Mindenki saját ízlése és igenyei szerint fejlessze tovább!!!

    • pl. CreateWindow-ban Title megadása
    • különböző propertyk mappelése/bindolása : Background, Margin, stb...
    • WindowList
    • rendezések : Tile,Horizontal,Vertical...
    • MinSize, MaxSize, Resizable...
    • Modal
    • stb...

    Demo forráskód:

    http://beta.devportal.hu/groups/silverlight/media/p/744.aspx