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

Blog


    October 22

    MEF, avagy készítsünk kiterjeszthető alkalmazásokat 0.

    Előbb vagy útóbb minden fejlesztő szembesül azzal a problémával, hogy olyan alkalmazást kellene írni, amely tulajdonképpen egy keretrendszer bizonyos alapfunkciókkal, amihez idővel újabb és újabb modulok kerülnek majd illesztésre.

    Ha ez egy zárt projekt, és mi vagyunk a fejlesztői a keretnek és a moduloknak is, akkor nem is biztos hogy “keretrendszert” építünk általános megoldásokkal, hanem egyszerűen add new project, add refenrece, aztán ‘jónapot, had ‘szóljon, hivatkozunk az új dolgokra, rebuild, már mehet is az új exe+dll valahogy a usereknek.

    Ez a megoldás gyors, egyszerű, ellenben ha sokan dolgozunk a projekten pl. *nd*aiakkal :D, és nem kellőképen dokumentált a fejlesztés módja, akkor egy nagy káosz kialakulásának leszünk jobb esetben csak szemtanui, rosszabb esetben résztvevői.
    Továbbá ez e megoldás kivitelezhetetlen, ha ténylegesen egy szuper-világmegváltó-hiperfrankó-keretrendszert alkottunk, pl. egy grafikai eszközt, egy hitel/pénzügyitermék-prortfóliót kezelő rendszert. És azt akarjuk hogy bárki a világon fejleszthessen hozzá (bizonyos feltételekkel) modulokat: egy új effectet, egy új terméket/jutalékszámító eszközt,stb.
    Ilyen esetben nem megoldás az hogy megkérjük a ‘világot hogy küldje már el a forráskódot, mi meg belefordítjuk… mert A usernek M modul, B usernek meg N modul kell, stb…

    Mit lehet tenni?

    Alkutunk egy saját leíró nyelvet: XML-ben össze lehet tenni a felületet, algoritmust, szabályokat, stb. Publikáljuk a “leírónyelvünk” specifikációját, és a modul futtatása nem más XML parszolás (saját parszerrel), majd futtatás…
    Egyszerű dolgoknál működik, sőt néha ez a “tökéletes” megoldás (pl workflow rule engine), de ugye látjuk a korlátait, nehézségeit?

    Nem marad más hátra, minthogy dll-t gyártatunk az önjelölt modulfejlesztőkkel, amit _nem_ teszünk be a keretalkalmazásba, és ezt a modult valahogy elérjük futási időben.
    Nos, ez ugye a Reflection .Net-ben.

    Milyen lehetőségeink vannak Reflection-el?

    • Nem adunk meg semmilyen kötöttséget, kivéve, hogy meghatározzuk 1<= “kulcs-pontot”. Ez lehet egy belépési pont, egy osztály, egy “programkód”,stb
      Pl. a keretalkalmazásunk nem tud mást mint, hogy az adott DLL-ben megkeresi az EztInditsdEl nevű (Form-ból származott) osztályt, példányosít egyet, és meghivja rajta a Show-t:
    • Assembly a = Assembly.LoadFile(dll_path);
                Type t = null;
                foreach (Type item in a.GetTypes())
                {
                    if (item.Name == "EztInditsdEl")
                    {
                        t = item;
                        break;
                    }
                }
      
                if (t!= null && t.BaseType == typeof(Form))
                {
                    ConstructorInfo ci= t.GetConstructor(new Type[0]);
                    object o= ci.Invoke(null);
                    Form f = o as Form;
                    f.MdiParent = this;                
                    f.Show();
                }
                else
                {
                    throw new Exception("hiba a modulban");   
                }

      Persze ezt lehet fokozni, konkrét osztálynevekkel, interfészekkel, attributumokkal, stb.
      Látszik, hogy nehézkes a használata: rengeteg reflection (minden egyes meghíváshoz, példányosításhoz, propertyhez,stb). ami ráadásul “lassú” is a late-binding miatt.
      Macera, megint.

    • A jól definiált “korlátokat”, kötelezettségeket határozunk meg, amiket be kell tartania a modul fejlesztőjének.
      Ezek tipikusan  baseclass-ok, interfészek, attribútumok, virtuális metódusok, stb…
      Ezeket betesszük egy külön DLL-be, mi adunk referenciát a DLL-re, és használjuk. A fenti megoldással szemben hatalmas az előnyünk. Reflection-t (szerencsés esetben) csak “egyszer” kell használnunk: a modul assembly betöltése után a “belépési” pont megkeresésésre és példányosítására.
      Utána már használhatjuk pl. a mi kis interfészünket.
      A modul fejlesztőjének nincs más dolga, mint az interfész megvalósítása (miután letöltötte az áltaunk publikussá tett DLL-t)

      Interfész:

      namespace ModulBase
      {
          public interface IModul
          {
              void Kiir(string mitirki);
      
              string Beolvas(string kerdes);
          }
      }
      Modul:
      namespace Modul1
      {
          public class MyModul:ModulBase.IModul
          {
              #region IModul Members
      
              public void Kiir(string mitirki)
              {
                  Console.WriteLine(mitirki);
              }
      
              public string Beolvas(string kerdes)
              {
                  Console.Write(kerdes);
                  return Console.ReadLine();
              }
      
              #endregion
          }
      }
      
      Alkalmazás:
    • Assembly a = Assembly.LoadFile(dll_path);
      ModulBase.IModul modul =null;
      foreach (Type item in a.GetTypes())
      {
         
          if ((item.GetInterface(typeof(ModulBase.IModul).FullName)) != null)
          {
              object o= item.GetConstructor(new Type[0]).Invoke(null);
              modul = o as ModulBase.IModul;
              break;
          }
      }
      
      if (modul != null)
      {
          modul.Kiir("alma");
          string valasz = modul.Beolvas("barack");
          Console.WriteLine(valasz);
      }
      else
      {
          throw new Exception("hiba a modulban");
      }

      Mint látható itt Reflection-t csak a példányosításra használjuk, utána már az interfészünkön keresztül programozzuk a modult.
      Halleluja. Mindkét fél élete egyszerű.
      De…

    (persze lehetne még fokozni, rengeteg egyéb ügyesebb-okosabb, de rosszabb megoldás is van…)

    Rengeteg olyan általános probléma van ami minden moduláris alkalmazásnál előjön. Tipikus generikus problémák amikre tipikus generikus válasz adható… Minek feltalálnunk újra a spanyolviaszt?!?!
    És mi van ha még a fenti intefész-DLL-t sem akarom odaadni, akarja használni a modul fejlesztője?!?!

    Ekkor jön be a MEF-Managed Extensibility Framework, amely választ ad ezen generikus problémákra, ill. előbb-útobb a .Net része lesz, vagyis elfogadottá válik, így máris megvan a “szerződés” a keret- ill. a modul-alkalmazás fejlesztője között…

    A témával egyelőre még nem foglalkoztam sokat, de amit láttam eddig az felkeltette az érdeklődésemet, így útjára indítok egy sorozatot, ami sorra veszi szépen a MEF-es fejlesztés mérföldköveit…

    October 20

    DragDrop Silverlight 3-ban

    Végre!
    Megérkezett a Drag&Drop támogatás a Silverlight 3-ban! Egészen pontosan a Silverlight Toolkit-ben!

    Bár még nem generikus a megoldás, de reméljük az lesz. Egyelőre a következők támogatják az adott vezérlőben a drag&drop-t:

    • ListBoxDragDropTarget
    • TreeViewDragDropTarget
    • DataGridDragDropTarget
    • DataPointSeriesDragDropTarget

    Használatuk ‘rémegyszerű:

    1. Silverlight Toolkit October telepítése
    2. Referenciát adunk a megfelelő silverlight és toolkit dll-ekre:
      c:\Program Files\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\System.Windows.Controls.dll
      c:\Program Files\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\System.Windows.Controls.Data.dll
      c:\Program Files\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\System.Windows.Controls.Data.Input.dll
      C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Toolkit\Oct09\Bin\System.Windows.Controls.Toolkit.dll
      C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Toolkit\Oct09\Bin\System.Windows.Controls.Data.Toolkit.dll
      c:\Program Files\Microsoft SDKs\Silverlight\v3.0\Libraries\Client\System.Windows.Data.dll
    3. Aztán kellenek ugye a névterek is:
    4. xmlns:mswindows="clr-namespace:Microsoft.Windows;assembly=System.Windows.Controls.Toolkit"
      xmlns:datatoolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Toolkit"                             
      xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"

    5. Ezek után használjuk a megfelelő DragDropTarget-et:
      toolkit:ListBoxDragDropTarget
      datatoolkit:DataGridDragDropTarget
      ….
    6. Ha egy elemet, pl. egy ListBox-t körbefogtunk egy ilyen elemmel, akkor máris tudunk belőle kiráncigálni elemet,elemeket:
      (az ItemsPanelTemplate felüldefiniálása, nem feltétlen szükséges, ez amiatt kell, hogy alapból virtualizált panel szerepel benne…)
    7. <toolkit:ListBoxDragDropTarget Name="lb1" >
               <ListBox Name="lb1">
                   <ListBox.ItemsPanel>
                       <ItemsPanelTemplate>
                           <StackPanel Orientation="Vertical" />
                       </ItemsPanelTemplate>
                   </ListBox.ItemsPanel>
               </ListBox>
           </toolkit:ListBoxDragDropTarget>
    8. Ahhoz  hogy Drop-ot is fogadjon engedélyezni kell ezt:

      <toolkit:ListBoxDragDropTarget mswindows:DragDrop.AllowDrop="True">
    9. És már csak egy megfelelő adatforrásra van szükségünk:

      var lst1 = new ObservableCollection<int>();
                lst1.Add(1);
                lst1.Add(2);
                lst1.Add(3);
                lb1.ItemsSource = lst1;
    10. Szokásos eseményekre természetesen mi magunk is reagálhatunk “kézzel”:
      DragEnter
      DragLeave
      DragOver
      Drop
      ItemDragStarting
      ItemDroppedOnSource
      ItemDroppedOnTarget

      Vagyis tudunk mindenféle jópofa UX dolgot csinálni, ill az ItemDragStarting el tudunk indítani egy elemet, ill a Drop-pal és a ItemDroppedOnSource/ItemDroppedOnTarget-el pedig reagálni tudunk mind a cél mind a forrás vezérlőn…

    Ha ezzel megvagyunk, akkor tökéletesen tudunk ide oda rángatni pl. ListBox-ból ListBoxba.

    dd

    A teljes kód (letöltés):

    <UserControl xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"  x:Class="sl3_drag_drop_demo.MainPage"
        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" 
                 
        xmlns:mswindows="clr-namespace:Microsoft.Windows;assembly=System.Windows.Controls.Toolkit"
        xmlns:datatoolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.Toolkit"                              
                 
        xmlns:toolkit="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Toolkit"
                 
        mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
        <Grid x:Name="LayoutRoot">        
            <Grid.ColumnDefinitions>
                <ColumnDefinition></ColumnDefinition>
                <ColumnDefinition></ColumnDefinition>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition></RowDefinition>
                <RowDefinition></RowDefinition>
            </Grid.RowDefinitions>
            <toolkit:ListBoxDragDropTarget Grid.Column="0"  mswindows:DragDrop.AllowDrop="True">
                <ListBox Name="lb1"   Background="LightBlue" MinWidth="50" MinHeight="150">
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Vertical" />
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                </ListBox>
            </toolkit:ListBoxDragDropTarget>
            <toolkit:ListBoxDragDropTarget Grid.Column="1" Drop="ListBoxDragDropTarget_Drop"  mswindows:DragDrop.AllowDrop="True">
                <ListBox Name="lb2" Background="LightYellow" MinWidth="50" MinHeight="150">
                    <ListBox.ItemsPanel>
                        <ItemsPanelTemplate>
                            <StackPanel Orientation="Vertical" />
                        </ItemsPanelTemplate>
                    </ListBox.ItemsPanel>
                </ListBox>
            </toolkit:ListBoxDragDropTarget>
            <datatoolkit:DataGridDragDropTarget  mswindows:DragDrop.AllowDrop="True" Grid.Row="1" Grid.Column="0" >
            <data:DataGrid Name="grd1" ></data:DataGrid>
                </datatoolkit:DataGridDragDropTarget>
            <datatoolkit:DataGridDragDropTarget  mswindows:DragDrop.AllowDrop="True" Grid.Row="1" Grid.Column="1" >
                <data:DataGrid Name="grd2"></data:DataGrid>
            </datatoolkit:DataGridDragDropTarget>
        </Grid>
    </UserControl>

    http://timheuer.com/blog/archive/2009/10/19/silverlight-toolkit-adds-drag-drop-support.aspx