Saturday, 24 January 2009

How to center a listview column in Grid mode

This one I’ve really struggled with. I have a lot of listviews in the application, and basically I love listviews in WPF, but as I wrote earlier, the downside of having total control is that you must control everything…

I have some columns that I wanted to be centred. Actually, this is not very easy. I’ve used Goggle a bit, but mostly I’ve read the MSDN documentation and now after three hours of testing I’ve come up with a solution. I’m not really satisfied yet, but it will work for now.

First I thought that there would be some sort of HorizontalContentAlignment property I could set. But there isn’t any there for columns. So I started to style the column in the listview that I want to centre like this.

<Style x:Key="OnlineHeaderStyle" 
               TargetType="{x:Type GridViewColumnHeader}" 
               BasedOn="{StaticResource 
TournamentColumnHeaderStyle}"
> <Setter Property="HorizontalContentAlignment" Value="Center"/> </Style> <DataTemplate x:Key="OnlineCell"> <DockPanel> <TextBlock Foreground="DarkBlue" HorizontalAlignment="Center" TextAlignment="Center"> <TextBlock.Text> <Binding Path="Publish" Converter="{StaticResource
converterBooleanTextConverter}"
/> </TextBlock.Text> </TextBlock> </DockPanel> </DataTemplate>

This should do it I assumed, but it wasn’t enough. I also had to set a style for the ListView.ItemContainerStyle, like this:

<ListView Name="lv_Tournaments" 
   View="{StaticResource GridViewTournament}">
   <ListView.ItemContainerStyle>
     <Style TargetType="ListViewItem">
      <Setter Property="HorizontalContentAlignment" 
Value="Stretch" /> </Style> </ListView.ItemContainerStyle> </ListView>

This has to be done because the Dockpanel, or all containers for a column cell has some sort of auto size, so if the cell is 100px wide, but the text of that cell if only 20px wide, the text container will be only 20px, and the textontainer is some how aligned to the left, and even tough the content is centred, it appears as left aligned, but with the HorizontalContentAlignment of the ItemcontainerStyle set to stretch the textcontainer will have a width of 100px, and the text will appear as centred. As i wrote in the beginning, I’m not total happy with this solution, but it is good enough for now. I can get it better, and will write about it then, and I also think that Microsoft has some things to develop regarding the Listview.

Labels: ,

kick it on DotNetKicks.com

Saturday, 10 January 2009

Disable a toolbar button image in WPF

When I worked with winforms, it was very easy to disable a toolbar button image, just set Enabled=False and your were done. With WPF on the other hand, it is a little bit more complicated.

I love WPF! You can do everything you couldn’t to in winforms. You have total control. But the downside of having total control is that you must control everything. :-)

So, to disable a toolbar button with an image, which I do via application commands, use this following trigger.

<Style TargetType="{x:Type Image}" x:Key="toolbarImageStyle">
            <Style.Triggers>
                <DataTrigger 
Binding="{Binding RelativeSource={RelativeSource AncestorType={x:Type Button}, 
AncestorLevel=1}, Path=IsEnabled}" Value="False">
                    <Setter Property="Opacity" Value="0.50"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>

And in the toolbar apply the style.

<Button 
 Name="btPlayersDelete" 
Command="commands:UserCommands.DeleteUsers" 
ToolTip="Delete players">
 
    <Image 
             Style="{StaticResource toolbarImageStyle}" 
             Name="imgPlayersDelete" 
Source="Images/Toolbar/delete.png"/> </Button>

Labels: ,

kick it on DotNetKicks.com

Saturday, 27 December 2008

BitmapSource och Image

Ok, ibland tänker jag för mycket… 
Jag använder ju nu ett renodlat WPF gränssnitt. I tidigare versioner jag påbörjade var det Winforms som gällde, och när det handlade om bildhantering så var det System.Drawing.Image objektet som jag använde för att hantera bilder, från databas upp till gränssnittet. Nu är det som sagt WPF som gäller och när man visar en bild i ett WPF-formulär är det istället en BitmapSource som används för att visa bilder. Objektet BitmapSource är en ny klass som använder DirectX för att rendera bilder, beroende på vilken hårdvara som finns i klienten och vilken version av DirectX som är installerad så hanterar denna klass detta automagiskt, och bättre prestanda erhålls för rendering av bilder.

Fantastiskt tänkte jag och började ändra min kod, från DAL till gränssnitt, dvs. jag bytte ut alla System.Drawing.Image objekt mot BitmapSource istället. Det var ju lättare sagt än gjort!
Först och främst, jag är så nöjd att jag kör TDD nu, all kod jag ändrade var täckt av unit tester, så listan över kompileringsfel var lång, och det var bara att kavla upp ärmarna. Ju mer jag jobbade med att implementera BitmapSource objektet för bilder desto mer skeptisk blev jag att det var rätt väg att gå. Jag var ju tvungen att lägga till referenser till PresentationCore.dll i DAL lagret vilket inte kändes helt ok. Jag googlade en hel del, men hittade inga bra källor som beskrev hur man ska hantera bilder från databasen upp till WPF-gränssnittet. Så när jag låg och försökte sova en natt tänkte jag på detta, och då slog det mig. P:et i WPF står för PRESENTATION, och då bestämde jag mig för att klasser som ingår i WPF har med presentationen att göra, inte dataacess, inte heller affärslogik. Så tillbaka till Visual Studio, och ändra tillbaka, återigen otroligt glad att jag har anammat TDD!

För att skicka en bild från databas till gränssnittet så har jag följande funktioner, mer än så behövs inte.

Först och främst, bilden är lagrad i Sql Server 2008 i ett Image fält, vilket sin tur översätts till System.Data.Linq.Binary. För att översätta detta till en Image har jag följande funktion:

public static Image ConvertLinqBinaryToBitmapImage(System.Data.Linq.Binary imageData)
        {
            if(imageData ==null)
                return null;
            using (MemoryStream pictureStream = new MemoryStream(imageData.ToArray()))
            {
                Image imageToReturn =Image.FromStream(pictureStream);
                return imageToReturn;
            }
        }

Och för att konvertera en Image till System.Data.Linq.Binary har jag följande funktion:

public static System.Data.Linq.Binary ConvertImageToLinqBinary(Image picture)
        {
            if(picture==null)
                return null;
            using(MemoryStream pictureStream = new MemoryStream())
            {
                picture.Save(pictureStream, picture.RawFormat);
                return pictureStream.ToArray();
            }
        }

Ovanstående kod används alltså för att konvertera bilden fram och tillbaka till mitt objekt som har en bild som property, när jag vill hämta eller spara till Linq To Sql objektet.

I WPF formuläret behövs ju Image klassen konverteras till en BitmapSource, och det finns det inbyggt stöd för. Följande funktion konverterar en System.Drawing.Image till BitmapSource:

private static BitmapSource CreateBitmapSourceFromImage(Image sourceImage)
        {
            if(sourceImage ==null)
                return null;
            System.Drawing.Bitmap bitmap = new Bitmap(sourceImage);
            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                bitmap.GetHbitmap(),
                IntPtr.Zero,
                Int32Rect.Empty,
                System.Windows.Media.Imaging.BitmapSizeOptions.FromEmptyOptions());
        }

Labels: , ,

kick it on DotNetKicks.com

Monday, 14 July 2008

WPF Elementhost

Nu börjar programmet bli klart för att betatestas av många fler personer. Todo-listan börjar krympa. Applikationen är alltså en Winforms applikation med vissa WPF-kontroller som integreras i WinForms med kontrollen elementhost. Microsoft hävdar att denna kontroll är perfekt att använda för att stegvis migrera en winform applikation till WPF eftersom man kan migrera kontroll för kontroll, eller ett formulär i taget, genom att skapa wpf-kontroller som man "hostar" i elementhost. Perfekt tänkte jag, den ska jag använda eftersom jag redan var klar med en stor del av gränssnittet och ville inte bygga om det jag redan gjort med Winforms, utan hellre fortsätta med WPF för det som inte är klart i programmet. Tyvärr har det visat sig att elementhost inte är helt ok, den läcker minne och uppträder segt vid resize händelser. Jag hade en usercontroll som visade grundläggande statistik för en spelare. Detta var alltså en vanlig winform userkontroll från början som jag sedan migrerade till en WPF-kontroll. Denna kontroll skapade jag en gång per användare i en panel så att det då visade fem sådana kontroller om det fanns fem användare i systemet. För att migrera detta skapade jag en WPF kontroll som ritade samma sak, och "hostade" den i en elementhost, som jag skapade en gång per användare i systemet. Till en början var jag nöjd med denna lösning, det gick fort att skriva om koden till detta, det enda jag behövde göra i grundapplikationen var att lägga till en elementhost med en WPF-userkontroll till befintlig applikationslogik. (Förutom själva koden för WPF-kontrollen så klart.) När jag sedan testade resize, olika antal kontroller (spelare), med mera, märkte jag hur applikationen blev segare och segare. Jag öppnade taskmanagern och ser att för varje resize ökade minnesanvändningen med ca 10MB, men den gick aldrig ner. Dessutom upplevde jag mycket flimmer när jag scrollade. Så, det var bara att ta tjuren vid hornen och göra som jag borde ha gjort från början, att skapa en userkontroll som dynamiskt hanterar antalet spelare, desutom blev det mycket snyggare också eftersom det finns fler möjligheter i WPF med expanders etc. Nedan är en bild på en WPF-userkontroll som visas i en elementHost, där WPF-userkontrollen laddar antalet spelare och skapar layouten dynamiskt i code-behind. Så en varning till alla som använder elementHost, var försiktig med att använda elementHost och tänk efter en gång extra innan du migrerar dina befintliga kontroller rakt av och om det är WPF eller Winforms som ska stå för det "dynamiska".

Labels: , ,

kick it on DotNetKicks.com