Krisztián's profileBátyai Krisztián[KRis]PhotosBlogListsMore ![]() | Help |
|
November 27 Silverlight 4 Printing/NyomtatásNyomtassunk Silverlight-ból! A Silverlight 4 egyik nagy újdonsága, amit mindeki kért hogy tudjon nyomtatni. Én nekem ezzel kapcsolatban vannak fenntartásaim, de ezt ugye már megbeszéltük a DevPortal-on…. Mondjuk amit most tud az SL az mindeképp jó, csak ne gondolja senki, hogy akkor el lehet felejteni a Rep.Serv. megoldásokat :D Tehát, kell valami adatforrás, amit megjelenítünk a feleületen…Ez legyen az AdventureWorks Product tábla.
private void btnPrintGrid_Click(object sender, RoutedEventArgs e) { popCM.IsOpen = false; //Print int page = 1; int record = 0; // create print document: PrintDocument printDoc = new PrintDocument(); printDoc.PrintPage += (s, args) => { double next_pos = 0; double next_height = 40; StackPanel spPrint = new StackPanel(); spPrint.Width = args.PrintableArea.Width; while (record < datas.Count && next_pos + next_height <= args.PrintableArea.Height) { StackPanel spH = new StackPanel(); spH.Orientation = Orientation.Horizontal; spH.Height = next_height; spH.Children.Add(new TextBlock() { Margin = new Thickness(0, 3, 3, 3), Text = datas[record].ProductID.ToString() }); spH.Children.Add(new TextBlock() { Margin = new Thickness(0, 3, 3, 3), Text = datas[record].Name.ToString() }); spH.Children.Add(new TextBlock() { Margin = new Thickness(0, 3, 3, 3), Text = datas[record].ListPrice.ToString("c") }); spH.Children.Add(new Rectangle() { Margin = new Thickness(0, 3, 3, 3), Width = datas[record].ProductID / 10, Fill = new SolidColorBrush(Colors.Red) }); spPrint.Children.Add(spH); record++; next_pos += next_height; } args.HasMorePages = record < datas.Count; args.PageVisual = spPrint; page++; }; printDoc.Print(); } Első esetben az kerül nyomtatásra, amit mi alkotunk kézzel-lábbal-kóddal. Látható hogy mi vagyunk felelősek azért hogy összerakjunk mindent!. A nyomtatás egyszerűen 3 lépésből áll (minimum):
Ennyi… nem is bonyolult. Forráskód: skydrive Mi van a DataGrid hátulján? (Készítsünk saját vezérlőt (Visual Studio-ban) VSM-el I/II)
Gondolom már mindenki elgondolkodott rajta mi van a DataGrid hátulján :) ugye?!?
De mi azt akarjuk!!! megtudni mi van ténylegesen a DataGrid hátulján.
Első körben mindent Visual Studio-val oldunk meg, második felvonásban pedig Blend 3-al.
Persze érdemes eljátszani az animációval, alignment-ekkel,stb… Forráskód: skydrive November 25 Instant Smooth HD tartalom elhelyezése weboldalunkra
Azt hiszem mindenki találkozott már olyan weboldallal amin ugyan volt média/video elhelyezve, csak éppen a minősége és/vagy a (lejátszás) felhasználói élménye a 0-t közelítette. Mi sem egyszerűbb hogy ezen túllépjünk: Smooth Streaming
Kell hozzá:
Lépések
using System; using System.Collections.Generic; using System.Text; using Microsoft.Expression.Encoder; using Microsoft.Expression.Encoder.Live; using Microsoft.Expression.Encoder.Profiles; using System.IO; namespace Live { class Program { static void Main(string[] args) { // MediaItem mediaItem = new MediaItem(@"forras.mp4"); //ezt kell használni a profile-ok összeállítására....aspect ratio tartásával Console.WriteLine("size={0}", mediaItem.VideoSize); AdvancedVC1VideoProfile videoProfile = new AdvancedVC1VideoProfile(); // When you create a VideoProfile you'll get one stream by default. // In this example remove that one as we’re going to explicity // add the three streams below. videoProfile.Streams.RemoveAt(0);
//!!!!
// a megfelelő méret és aspect ration megadását meg kell csinálni,
// a forrás méretei alapján
// pl. érdemes puskázni az Expression Encoder default beállításaiból
// pl. ~3mbps, ~1600kbps, ~1200kbps, ~800kbps, ~500kbps
// hozzá a megfelelő arányos méretekkel...
//!!!!
//videoProfile.Streams.Add( // new VariableConstrainedBitrate(1450, 1600), // new System.Drawing.Size(800, 600)); videoProfile.Streams.Add( new VariableConstrainedBitrate(1050, 1600), new System.Drawing.Size(640, 480)); videoProfile.Streams.Add( new VariableConstrainedBitrate(600, 1600), new System.Drawing.Size(400, 300)); // Use smooth streaming with automatically sized streams. videoProfile.SmoothStreaming = true; videoProfile.Streams.AutoSize = true; mediaItem.OutputFormat.VideoProfile = videoProfile; Job job = new Job(); job.MediaItems.Add(mediaItem); // Set up the progress callback function job.EncodeProgress += new EventHandler<EncodeProgressEventArgs>(OnProgress); // Set the output directory and encode. job.OutputDirectory = "c:\\temp"; job.CreateSubfolder = true; job.Encode(); } static void OnProgress(object sender, EncodeProgressEventArgs e) { Console.WriteLine(e.Progress); } } } Info : http://blogs.msdn.com/expressionencoder/archive/2009/07/29/9853000.aspx November 23 Azure © oktatási anyagokMindamellett hogy a PDC-ről letölthető számos előadás ill. a gyári blogokban kezdenek szállingózni a demok, oktatási példák, egy egész komoly anyag jelent meg a CH9-en is Azure-al kapcsolatban. (Step By Step jellegű, elég részletesek) November 22 Ingyenes Ebook
Találtam egy ingyenes könyvet, ennek apropóján gondoltam összeszedem, összeszedhetjük az ingyenes ebook-okat, amik elérhetők .Net technológiákról:
Majd megjegyzésbe megy a többi… November 21 Silverlight 3 – WCF-binary formatter
Tudom tudom a 3 már történelem :D, de már az is tudta, hogy nem Soap borítékban mindenféle buta enkódlásban küldük át a bináris adat a kliensre, hanem binárisan. Ez persze alap a WCF-ben, de egy 3-4mbyte-os pluginban nem volt magától érthetődő (gondolom én), ezért pl. a SL2 nem is tudta (maradt a jó öreg basicHttpBinding-> HTTP+SOAP). Most egy (még) titkos projekt keretében bináris adatok kellene Server-Kliens kommunikációban Silverlight-ba küldeni, és azt tudtam hogy “komolyabb” binding-ot nem tud az SL, de rájöttem (rákerestem Google-ben és Bing-ben) hogy építeni tudok sajátot, így azzal megoldható a történet :
<customBinding> <binding name="binaryHttpBinding"> <binaryMessageEncoding> </binaryMessageEncoding> <httpTransport> </httpTransport> </binding> </customBinding> …
<endpoint address="" binding="customBinding" bindingConfiguration="binaryHttpBinding" contract="IService"> <identity> <dns value="localhost" /> </identity> </endpoint>És kész… lőn bináris kommunikáció HTTP felett Silverlight-ban! 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. 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… 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. Milyen lehetőségeink vannak Reflection-el?
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. A jól definiált “korlátokat”, kötelezettségeket határozunk meg, amiket be kell tartania a modul fejlesztőjének. namespace ModulBase { public interface IModul { void Kiir(string mitirki); string Beolvas(string kerdes); } } 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. (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?!?! 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-banVégre! 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:
Használatuk ‘rémegyszerű:
xmlns:mswindows="clr-namespace:Microsoft.Windows;assembly=System.Windows.Controls.Toolkit" <toolkit:ListBoxDragDropTarget Name="lb1" > <ListBox Name="lb1"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <StackPanel Orientation="Vertical" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> </toolkit:ListBoxDragDropTarget> Ha ezzel megvagyunk, akkor tökéletesen tudunk ide oda rángatni pl. ListBox-ból ListBoxba. 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 June 19 .Net 2.0-3.5 AKCIÓS képzések @ NetAcademiaSziasztok! Engedjétek meg, hogy közzétegyek egy kis reklámot. Aki (vagy a cég ahol dolgozik) esetleg mostanában tervezett .Net képzést, annak figyelmébe ajánlom a következő tanfolyamokat:
A fenti táblázatban szereplő tanfolyamok ára a tanfolyam hosszától és a megrendelés leadásának időpontjától függ:
Július 1. után a „fapados” jelentkezési lehetőség véget ér, a tanfolyamokra ezt követően az adott tanfolyam listaárán lehet regisztrálni. SA vouchert elfogadunk! Bővebb információ: www.netacademia.net/nyariakcio April 17 MVPEgy kicsit csendben voltam blog ill. cikk írás dologban az elmúlt hónapokban. Az elmúlt egy-másfél én munkája, köztük a fenti rendezvények, alapján a Microsoft érdemesnek tartott arra hogy tagja legyek a magyarországi MVP csapatnak. Hogy mi is az az MVP cím?!?
A program keretében ezentúl számos dologhoz hozzáférhetek a publikussá válása előtt (persze mindenféle NDA mellett), könnyen kapcsolatot teremthetek mindenféle Product Grouppal az Olimposzon ( Seattle-Redmond-Microsoft HQ), talán még el is juthatok oda jövőre, és még sok minden egyéb… Remélem, hogy így még hasznosabban tudok majd részt venni a hazai .Net életben…
És a lényeg : Rendezvény, meghívó, Azure, Silverlight, Sync Services
Egy Május 4.-én Szegeden megrendezésre kerülő eseményre szeretném felhívni a figyelmet, ahol lesz szerencsém előadni egy témában. Akit érdekelnek a következő témák, és Szeged közelében lakik az jöjjön el:
Az előadásokat Bátorfi Zsolt barátom fogja felvezetni a szokásos :) ‘mivel fogunk találkozni a következő hónapokban, évben a platformon és körülötte témával. Helyszín és időpont: További részletek, jelentkezés : http://devportal.hu/content/usergroupszeged.aspx Fejlesztői MCP (Microsoft Certified Professional)Get certified… ‘Hanem az, hogy legtöbbször nincsenek tisztában az emberek a vizsgákkal, azok egymásra épülésével, ill. hogy ezek milyen kapcsolatban vannak a minősítésekkel. Az is kicsit zavaros hogy milyen kapcsolatban van a 2.0 és 3.0/3.5. Honnan érdemes kezdeni. Lássuk! Érdemes az elején leszögezni, hogy a 2.0-s dolgokra szükség van. Leszámítva egy két dolgot, gyakorlatilag tekinthetjük úgy, hogy a 3.0-3.5 az egy szép nagy kiegészítése a 2.0-nak, megfordítva a 3.0-3.5 a 2.0-ra épül. Képzeljük magunkat egy olyan ember helyzetébe, aki most ismerkedik a .Net-tel, most akar elmélyedni benne, szakemberré válni. Hogy egy frissen végzett egyetemistáról, egy áttérő/megtérő :) Delphi-s/JAVA-s ról van szó, vagy hobbyprogramozóról, az mindegy. Tehát e leendő szakember rendelkezik általános ismeretekkel, és van fogalma arról hogy mi az a programozás, mi az a ciklus, és alap OOP ismeretekkel is rendelkezik. Természetesen önálló tanulással, könyvekkel, gyakorlással, munkahelyen tapasztalatszerzéssel,
Ugyan a vizsga nem olcsó (pláne a tanfolyam), de én úgy gondolom hosszútávon, esetlegesen külföldi munkavállalásban gondolkodva mindenképp előny (lehet). Egyéb infók :
Ha van kérdése valakinek, ne tartsa vissza!!! January 22 Saját LINQ provider készítése (LINQ 2 MyWebService)Szerény LINQ tapasztalataim azt mutatják (nem reprezentatív, meg persze a kivételek…), hogy az emberek nagy többsége nem úgy közelíti meg a LINQ-t ahogy azt kellene (szerintem). A legtöbb ember fejében a következő egyenlőség él : LINQ == LINQ 2 Sql Máshogy fogalmazva a LINQ 2 Sql maga a LINQ. Hogy ez miért van így az egy másik érdekes kérdés, részben mert azzal találkozik az ember először, mert ezzel demóznak mindenhol, és mert legtöbbször adatbázissal dolgozunk és ekkor adódik ugye maga LINQ 2 SQL. Furcsállják is a tanfolyam-hallgatók, amikor a 2 napos LINQ tanfolyam első felében LINQ 2 Object-ről beszélünk, saját providert írunk, stb… Mi köze ennek a blogbejegyzés címében előrevetített témához?!? Tehát mielőtt tovább olvasnád, javaslom ezen témakörök átbogarászását:
Ha ez megvolt… akkor bele is vághatunk. LINQ 2 akármiből tízesével lehet találni a google-ön. De hogy?!? Első gyors megoldás, hogy kiterjesztjük az osztályunkat IEnumberable- interface-el, és máris LINQ alatt felhasználhatóvá vált az osztály query irására. Előnyei : mindösszesen 2 perc szükséges fejlesztés. Hátrányai : látszólagos, és sok esetben nem skálázható, nem finomítható… Persze ettől még sok esetben jó megoldás lehet. Ha pl. az adatforrás, ahonnan kapjuk az adatokat egy hagyományos WS, ami XML-ben adja vissza az adatokat, és a feladat az, hogy mi ne “lássuk” a WS-t, csak egy halom(List<>) adatot kapjunk, akkor adódik a megoldás hogy készítünk egy wrapper osztályt, ami megvalósítja a fent említett interface-t. Mégpedig úgy hogy lekéri az összes terméket az összes tulajdonságával… mindig. Aztán majd a LINQ query elmolyol rajta: leszűri amire kell, és kiválogatja a megfelelő tulajdonságokat… public class ProductSearch_bad : IEnumerable<Product> { #region IEnumerable<Product> Members public IEnumerator<Product> GetEnumerator() { return (IEnumerator<Product>)((IEnumerable)this).GetEnumerator(); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { ProductWS.Service1SoapClient client = new Linq2WebService.ProductWS.Service1SoapClient(); string ret = ""; try { //fontos hogy mindig az összes lejön!!!! ret = client.GetAll(); } catch (Exception ex) { } XDocument xdoc = XDocument.Parse(ret); var q = from x in xdoc.Descendants("product") select new Product() { ProductID = int.Parse(x.Attribute("ProductID").Value), Name = x.Element("Name").Value, ProductNumber = x.Element("ProductNumber").Value, ListPrice = x.Element("ListPrice").Value }; Console.WriteLine("WS : letöltöttem {0} db terméket",q.Count() ); return q.GetEnumerator(); } #endregion } Használata: Console.WriteLine("Most jól elkérjük a WS-től az összeset:"); ProductSearch_bad search_bad = new ProductSearch_bad(); var qq = from pp in search_bad where pp.Name.Contains("b") select pp; foreach (var item in qq) { Console.WriteLine(item.Name); } Ugye mindenki látja hogy miért nem jó (2 kell de 20 jön át) a megoldás? Annak ellenére hogy a (kódot) “felhasználó” programozó örül majd mind majom a fa…nak, mert sose volt még ilyen egyszerű adatelérése. A megoldás adódik: valahogy át kell juttatni a feltételeket, és a kért mezőket, a “túloldalra”, ami ezek után csak tényleges adatokat adja majd vissza. Ehhez elég lesz néhány osztály és pár 10 sor kód. A kódolást több oldalról el lehet kezdeni (TOP-Down, Down-TOP), én az elejéről kezdem. 1.Kell egy WS ami tud feltételeket kezelni, és mezőket válogatni: [WebMethod] public string GetProducts(string name,string productnumber,string cols) { AdventureWorksDataContext dc = new AdventureWorksDataContext(); XDocument xdoc; xdoc = new XDocument(new XElement("Products", (from p in dc.Products where (String.IsNullOrEmpty(name) || p.Name.ToLower().Contains(name.ToLower())) && (String.IsNullOrEmpty(productnumber) || p.ProductNumber.ToLower().Contains(productnumber.ToLower()))
select ProductSelector(cols,p)
)
));
return xdoc.ToString();
}
private object ProductSelector(string cols, Product p)
{
XElement ret = new XElement("product",
new XAttribute("ProductID", p.ProductID)
);
if (cols.Contains("Name")) ret.Add(new XElement("Name", p.Name));
if (cols.Contains("ProductNumber")) ret.Add(new XElement("ProductNumber", p.ProductNumber));
if (cols.Contains("ListPrice")) ret.Add(new XElement("ListPrice", p.ListPrice));
return ret;
}
Látható hogy a WS a feltételeknek megfelelően fog csak adatokat visszadni, sőt! csak az adott oszlopokat fogja visszaadni! 2/1. Kliens oldal Így akarjuk használni : var q = from p in new ProductSerach() where p.Name == "Race" && p.ProductNumber == "1" select new { p.Name }; foreach (var item in q) { Console.WriteLine(item.Name); }
2/2. Segéd osztály1 public class Product { public int ProductID { get; set; } public string Name { get; set; } public string ProductNumber { get; set; } public string ListPrice { get; set; } } 2/3 Segéd osztály2 public class ProductSerach : IEnumerable<Product> { private ProductQueryCriteria _criteria; //default private string cols = "Name|ProductNumber|ListPrice"; public ProductSerach Where(Expression<Func<Product, Boolean>> predicate) { _criteria = new ProductSearchExpressionVisitor().ProcessExpression(predicate); return this; } public ProductSerach Select<TResult>( Expression<Func<Product, TResult>> selector) { cols = new ProductSelectExpressionVisitor().ProcessExpression(selector); return this; } #region IEnumerable<Product> Members public IEnumerator<Product> GetEnumerator() { return (IEnumerator<Product>)((IEnumerable)this).GetEnumerator(); } #endregion #region IEnumerable Members System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { IEnumerable<Product> prods = ProductHelper.PerformQuery(_criteria,cols); return prods.GetEnumerator(); } #endregion } public class ProductQueryCriteria { public string ProductNumber { get; set; } public string Name { get; set; } } Mi van ebben az osztályban?!?
Tehát ez az osztály bitosítja az enumerálhatóságot, szép magyarosan. 2/4. Segéd osztály3 Fel kellene dolgozni magát a Where részt: public class ProductSearchExpressionVisitor { ProductQueryCriteria _Criteria; public ProductQueryCriteria ProcessExpression(Expression expression) { _Criteria = new ProductQueryCriteria(); VisitExpression(expression); return _Criteria; } private void VisitExpression(Expression expression) { if (expression.NodeType == ExpressionType.AndAlso) { VisitAndAlso((BinaryExpression)expression); } else if (expression.NodeType == ExpressionType.Equal) { VisitEqual((BinaryExpression)expression); } else if (expression is LambdaExpression) { VisitExpression(((LambdaExpression)expression).Body); } } private void VisitAndAlso(BinaryExpression andAlso) { VisitExpression(andAlso.Left); VisitExpression(andAlso.Right); } private void VisitEqual(BinaryExpression expression) { if ((expression.Left.NodeType == ExpressionType.MemberAccess) && (((MemberExpression)expression.Left).Member.Name == "ProductNumber")) { if (expression.Right.NodeType == ExpressionType.Constant) _Criteria.ProductNumber = (String)((ConstantExpression)expression.Right).Value; else if (expression.Right.NodeType == ExpressionType.MemberAccess) _Criteria.ProductNumber = (String)GetMemberValue((MemberExpression)expression.Right); else throw new NotSupportedException("Expression type not supported for ProductNumber: " + expression.Right.NodeType.ToString()); } else if ((expression.Left.NodeType == ExpressionType.MemberAccess) && (((MemberExpression)expression.Left).Member.Name == "Name")) { if (expression.Right.NodeType == ExpressionType.Constant) _Criteria.Name = (String)((ConstantExpression)expression.Right).Value; else if (expression.Right.NodeType == ExpressionType.MemberAccess) _Criteria.Name = (String)GetMemberValue((MemberExpression)expression.Right); else throw new NotSupportedException("Expression type not supported for Name: " + expression.Right.NodeType.ToString()); } } } (fontos dolgok kiemelve) Elsőre bonyolult, másodikra még inkább. Sajna át kell látni az Expression-t A-Z-ig. (Albert István) Ezt az igények szerint el lehet bonyolítani… 2/5. Segéd osztály4 Select feldolgozása: public class ProductSelectExpressionVisitor { string cols; public string ProcessExpression(Expression expression) { cols = ""; VisitExpression(expression); return cols; } private void VisitExpression(Expression expression) { if (expression is LambdaExpression) { VisitExpression(((LambdaExpression)expression).Body); } if (expression is NewExpression) { VisitNewExpression((NewExpression)expression); } } private void VisitNewExpression(NewExpression newExpression) { //EZ NEM A TELJES MEGOLDÁS, CSAK EBBEN AZ ESETBEN működik //ez csak abban az esetben működik, ha az anonymous tipusban CSAK a product osztály // tulajdonságai vannak, azonos névvel!!!! //amilyen bonyolult Select-et akarunk írni, úgy kell "elbonyolítani" a bejárást... foreach (var item in newExpression.Constructor.ReflectedType.GetProperties()) { cols += "|" + item.Name; } } private void VisitAndAlso(BinaryExpression andAlso) { VisitExpression(andAlso.Left); VisitExpression(andAlso.Right); } } Ez CSAK abban az esetben működik, ha anonymous tipust akarunk csinálni belőle, a megfelelő mezőket válogatva…. 2/6. Lekérdezés elvégzése: public static class ProductHelper { static internal IEnumerable<Product> PerformQuery(ProductQueryCriteria criteria, string cols) { ProductWS.Service1SoapClient client = new Linq2WebService.ProductWS.Service1SoapClient(); string ret = ""; try { ret = client.GetProducts(criteria.Name, criteria.ProductNumber, cols); } catch (Exception ex) { } XDocument xdoc = XDocument.Parse(ret); var q = from x in xdoc.Descendants("product") select CreateProductFromCols(cols,x); return q; } private static Product CreateProductFromCols(string cols,XElement x) { Product ret = new Product() { ProductID = int.Parse(x.Attribute("ProductID").Value) }; if (cols.Contains("Name")) ret.Name = x.Element("Name").Value; if (cols.Contains("ProductNumber")) ret.ProductNumber = x.Element("ProductNumber").Value; if (cols.Contains("ListPrice")) ret.ListPrice = x.Element("ListPrice").Value; return ret; } } Miután megvan a criteria és a cols változó, ami kell a lekéréshez, meg tudjuk hívni a megfelelő WS-t. 3. Testre szabás… Az igények szerint :)
Összefoglalva: a lényeg hogy a kliens oldali felhasználás egyszerű, és mindig csak a megfelelő adatokon dolgozik, egy hidat képeztünk a rétegek között a LINQ kiterjesztésével. Persze ez még mindig csak IEnumberable, és ne IQueryable, ami között az a nagy különbség, hogy ha az készítünk egy query-t majd azt felhasználjuk egy másikban, akkor késleltetett végrehajtás ide vagy oda, az első query le fog futni önmagában… függetlenül a másodiktól. Alapötlet : LINQ 2 Amazon Forrás : http://devportal.hu/groups/linq/media/p/5855.aspx demo adatbázis : Microsoft AdventureWorks http://www.codeplex.com/SqlServerSamples December 22 C# && VB.Net coding standards Sokszor van arra szükség, akár ha egyedül dolgozunk, akár 10-20-n egy csapatban, hogy kialakítsuk a "saját" elnevezési konvenciónkat, kódolási szabályzatunkat. Sokszor elég egy másik által készítettből kiindulni... (ez nem plágium, pláne ha a készítője osztja meg a nagyvilággal)
KRis December 19 LINQ 2 SQL null kérdések…
Egy fontos dologra bukkantam, ami nem biztos hogy elsőre egzakt, legalábbis nekem nem volt az… LINQ 2 SQL használat közben adatbázis oldalon minden lehet null.
mire fog ez lefordulni?!?!
Helyes! ezt vártuk!
mire fog ez lefordulni?!?!
Helyes!?!? A francokat! Mi a következtetés?!? Hát ezt nem szabad használni!
Ez pedig a megfelelő SQL-t fogja minden esetben tartalmazni. (Vagy is null, vagy = @p) Aki nem hiszi járjon utána! December 05 Paralell C#
Kezdtem mostanában már XAML-el álmodni... gyakorlatilag mostanában csak SL2-vel, WPF-el és WF-val dolgoztam... Úgy gondoltam kicsit visszakanyarodok az alapokhoz : C# és a paralellizmus... Miről is van szó? Ma már gyakorlatilag történelem az egy processzort/magot tartalmazó gépek, tehát adott a párhuzamosítás lehetősége architekturális szinten. Vagyis ha akarunk többszálú alkalmazást, akkor írjuk meg mi kézzel! mindent! Tehát akkor hogyan nem? Itt egy példa : brute force hash gyártás :))) (természetesen ez csak egy teszt "alogritmus"/állatorvosiLÓ, biztos van jobb,gyorsabb... lehet hibás is... ;-) ) public static void Feltor(object pars) Ezt lehet indítani 1-N szálon: public static void Keres(int hossz,int szalak_szama) Teszt után egyértelműen kiderül hogy a 2 szál gyorsabb mint az 1 :) A 4 meg több mint a 2 :) Az első teszt egy CORE2 DUO 7300-al ment. Nos, ez volt ugye az explicit, hagyományos szálkezelés.
Alakítsuk át a kódót a következő módon:
A lényeg hogy bár nem teljesen implicite kezelünk több szálat, mert le kell cserélni a for ciklust a Paralell.For-ra, és mint lambda kifejezés/anonymous-delegate kell átadnunk a ciklus törzsét. DE sehol nem kell leírni a Thread szót, és még több processzoron kerül végrehajtásra a ciklus: ------------------------ Mi történik a háttérben? "valamilyen algoritmus" alapján a mögöttes kód(Paralell.For implementációja) eldönti hogy mennyi szálon kellene futtatni (mi lenne az optimális) a ciklust, és hasonlóan ahogy mi felszabdaltuk a ciklust X részre, úgy tesz a Paralell.For is... Persze fontos hogy a ciklusban végrehajtott action ne akarjon olyan erőforráshoz hozzáférni, amelyhez egy esetleges másik szálon vele párhuzamosan futó ciklustörzs is hozzá akar férni. ----------------------- A másik része a Paralell Extension-nek a Paralell LINQ. Nézzünk rá egy példát: public static bool IsPrime(int i) ... }.... int l = 500000; Eredmény: Ha nem is dupljája de látható hogy egy LINQ lekérdezésen is lehet gyorsítani. Rengeteg példa, és maga CTP is letölthető innen: http://msdn.microsoft.com/en-us/concurrency/default.aspx Forráskód : http://devportal.hu/groups/fejlesztk/media/p/4256.aspx Annyi screencast-ot ígértem már mostanában (amelyek hamarosan jönnek), hogy nem merem megígérni, hogy ebből is lesz egy... talán ha CTP-ből valami magasabb fázisba lép a projekt... -- September 12 Rolling 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). (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?!? 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/ -- 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?
Oldjuk fel a problémákat!! Source megadása
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). {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:
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... 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. 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
Ehhez alakítsuk ki a felületünket: <Grid.RowDefinitions> Cím: <StackPanel Grid.Row="0" Grid.Column="0" HorizontalAlignment="Center"> <TextBlock>Title</TextBlock> </StackPanel> Mozgatás: <Rectanglex:Name="rectDrag"MouseLeftButtonDown="rectDrag_MouseLeftButtonDown" Bezárás: <Button x:Name="btnClose" Content="X" Grid.Column="1" Grid.Row="0" 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" 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! Díszítés: <Border BorderThickness="2" Grid.ColumnSpan="2" Grid.RowSpan="2" 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... 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. És voálá... Mindenki saját ízlése és igenyei szerint fejlessze tovább!!!
Demo forráskód: http://beta.devportal.hu/groups/silverlight/media/p/744.aspx |
|
|