• This is a usual time of the year for summaries so let’s keep the tradition alive and write one. Here’s my 2017 achievements split between months. January Blogging for 18 days straight – nothing near gutek’s achievement (whole year!) but still a nice streak Microsoft MVP title (thx Konrad Kokosa for pointing this one ;)) February […]

  • “Advent of Code is a series of small programming puzzles for a variety of skill levels.” Each day was a fun an interesting coding challenge. I’ve decided to practice and code this in python to learn the skill. Some of them might not be the best python scripting as I was short on time in […]

  • Some time ago I’ve attended a .net developer days 2017 conference. I was quite busy since (traveling, teaching .net, working) that only now I got some time to share some thoughts about it. As a bonus I’m including a short interview I did during the event. I need to state here, just to be clear, […]

  • We developers don’t like documentation. We don’t like to read it, and we even more we don’t like to write it. But sometimes it is worth to read it. Like when you find out that by using datetime in SQL DB you got a milliseconds precision but only if it ends on 0,3,7 (link). That […]

  • In the previous post we’ve removed some of the technical debt that could be found in our NetDeveloperPoland Website application. In this one we will remove it even more. We can even maybe reach a B? Let’s see where we’ll end up at the end of this part.

Oj już prawie miesiąc odkąd cokolwiek pojawiło się na blogu. Nie będę się tłumaczył z tego faktu, a tylko powiem, że kolejne wpisy nie będą pojawiać się często. Jako, że ostatnimi czasy siedzę w WPF to dziś wpis także z tej kategorii.
Może nie będą to takie tajniki jak w tytule zapowiadam, ale raczej zwrócenie uwagi na pewną funkcjonalność DependencyProperty. Czasami prócz samej możliwości bindowania do takiej właściwości chcemy także uzyskać np. odmalowanie kontrolki, która daną właściwość definiuje. Ostatnio miałem właśnie taką potrzebę i ten wpis jest właśnie spuścizną na przyszłość, abym nie musiał tego szukać.
Wszyscy wiedzą (a jeśli nie to szybciutko należy się dowiedzieć), że podstawowa definicja DependencyProperty to mniej więcej taki kod.

public static DependencyProperty LengthProperty = DependencyProperty.Register(“Length”, typeof (int),
                                                                              typeof (Window1));
public int Length
{
    get { return (int) GetValue(LengthProperty); }
    set { SetValue(LengthProperty, value); }
}

Jeśli jednak chcemy wyciągnąć dodatkowe możliwości DependencyProperty pamiętajmy, że funkcja Register posiada jeszcze dodatkowy 4ty i 5ty parametr! Co można za ich pomocą uzyskać? Zobaczmy jak wygląda czwarty parametr.

PropertyMetadata

Parametr PropertyMetadata to klasa bazowa pozwalająca na zdefiniowanie dodatkowych opcji naszej właściwości. Najniżej w hierarchii znajduje się klasa FrameworkPropertyMetadata i na opisie jej się skupimy. Co możemy zdefiniować przy jej użyciu?

  • wartość domyślną (defaultValue)
  • dodatkowe parametry (FrameworkPropertyMetadataOptions)
  • funkcję wołana w przypadku zmiany wartości (PropertyChangedCallback)
  • funkcja wołana w przypadku wymuszenia przeliczenia wartości (CoerceValueCallback)

Odnośnie wartości domyślnej nie trzeba się rozpisywać, więc przejdźmy do kolejnego punktu tajemniczo oznaczonego dodatkowe parametry.
Za ich pomocą możemy powiedzieć WPF, na co wpływa zmiana wartości naszej właściwości. Np. można tę wartości ustawić na FrameworkPropertyMetadataOptions.AffectsRender. Dzięki temu, za każdym razem przy zmianie wartości zostanie wywołana funkcja OnRender. Pozostałe ciekawe właściwości, które za pomocą możemy ustawić to: AffectsArrange, AffectsMeasure, BindsTwoWayByDefault (opis wszystkich – FrameworkPropertyMetadataOptions)
Trzeci i czwarty parametr pozwalają na zdefiniowanie funkcji, które będą wołane odpowiednio w przypadku zmiany wartości lub wymuszenia jej przeliczenia (za pomocą metody CoerceValue).
Przykład:

public static DependencyProperty LengthProperty =
                                DependencyProperty.Register(“Length”, typeof (int),typeof (Window1),
                                new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsArrange)
                                                             );

Dzięki temu za każdym razem gdy zmieni się wartość zmiennej Length WPF automatycznie wywoła przeliczenie layoutu.

ValidateValueCallback

Za pomocą tego parametru możemy zdefiniować metodę, w której będziemy mogli sprawdzić czy nowa wartość dla naszej właściwości spełnia reguły walidacyjne. Dzięki temu możemy napisać taki kawałek kodu:

public static DependencyProperty LengthProperty =
                                DependencyProperty.Register(“Length”, typeof (int),typeof (Window1),
                                                   new FrameworkPropertyMetadata(1,
                                                   FrameworkPropertyMetadataOptions.AffectsArrange), Validate);
public static bool Validate(object val)
{
    if (val == null)
        return false;
    if (val.GetType() != typeof(int))
        return false;
    int value = (int) val;
    return value > 0 && value < 10;
}

który zostanie wywołany przy każdej zmianie wartości. W przypadku zwrócenia wartości false, ArgumentException zostanie wyrzucony, tak więc musimy być przygotowani na jego obsłużenie.

public int Length
{
    get { return (int) GetValue(LengthProperty); }
    set
    {
        try
        {
            SetValue(LengthProperty, value);
        }
        catch (ArgumentException ex)
        {
            //obsługa błędnej wartości               
        }               
    }
}

Tyle na dziś. Mam nadzieję, że komuś oszczędzi to trochę szukania. Do następnego razu – kiedykolwiek on będzie :).

W ostatnią sobotę miałem przyjemność wygłosić sesję na Visual Studio 2010 Community Launch w Krakowie zatytułowaną “Programy równoległe są dla każdego”.

Dla zainteresowanych poniżej zamieszczam swoje materiały.

Prezentacja
Przykłady

Miłego oglądania.

Ostatnio męczyłem się z jednym tematem z WPF’a a mianowicie stworzyć przycisk, który jako swoje stany (Normal, Pressed, Disabled, Hover) będzie miał obrazki. Chciałem to zrobić ze zwykłego przycisku odpowiednio tworząc jego Template i wykorzystując potęgę Triggerów. Wszystkie moje próby spaliły na panewce bo i problem już się pojawiał w momencie gdzie przechowywać ścieżki do 4 obrazków.
W końcu przyszedł pomysł (wsparty przejrzeniem SO – dzięki @Gutek) i zabrałem się za robienie CustomControl. Ten post jest na przyszłość no i dla innych jakby ktoś potrzebował – taki Step By Step.
Zaczynamy zatem od stworzenia nowej klasy – nazwijmy ją MenuButton. Możemy też dodać od razu CustomControl (WPF). Wtedy od razu dostaniemy odpowiedni wpis w konstruktorze:

            DefaultStyleKeyProperty.OverrideMetadata(typeof(MenuButton),
                        new FrameworkPropertyMetadata(typeof(MenuButton)));

Oraz odpowiedni wpis w Generic.xaml w folderze Themes.

    <Style TargetType=”{x:Type local:MenuButton}”>
        <Setter Property=”Template”>
            <Setter.Value>
                <ControlTemplate TargetType=”{x:Type local:MenuButton}”>
                    <Border Background=”{TemplateBinding Background}”
                           BorderBrush=”{TemplateBinding BorderBrush}”
                           BorderThickness=”{TemplateBinding BorderThickness}”>
                    Border>
                ControlTemplate>
            Setter.Value>
        Setter>
    Style>

Dzięki temu nasza kontrolka straciła właśnie swój wygląd. I dobrze. Zabierzmy się zatem z powrotem do pracy. Tak więc podsumowując nasza klasa na chwilę obecną wygląda tak:

    public class MenuButton : Control
    {
        static MenuButton()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(MenuButton),
                        new FrameworkPropertyMetadata(typeof(MenuButton)));
        }
    }

Teraz potrzebujemy mieć miejsce gdzie będziemy przechowywać ścieżki do naszych obrazków. Aby móc je ładnie bindować i wykorzystywać całą potęgę WPFa zrobimy je jako DependencyProprty.

        public static DependencyProperty NormalStateImageProperty;
        public string NormalStateImage
        {
            get { return (string)GetValue(NormalStateImageProperty); }
            set { SetValue(NormalStateImageProperty, value); }
        }

Oraz zarejestrujmy ją w konstruktorze.

NormalStateImageProperty = DependencyProperty.Register(“NormalStateImage”,
    typeof(string), typeof(MenuButton));

I powtarzamy operację z pozostałymi 3-ma stanami: Hover, Pressed oraz Disabled. Zanim jednak przejdziemy do definiowania wyglądu zobaczmy jak będziemy mogli dodawać nasz przycisk:

<controls:MenuButton Height=”24″ Width=”24″ 
                    NormalStateImage=”/Images/zoom_in_normal.png”
                    HoverStateImage=”/Images/zoom_in_hover.png”
                    PressedStateImage=”/Images/zoom_in_clicked.png”
                    DisabledStateImage=”/Images/zoom_in_disabled.png” x:Name=”btn” />

Czyli dość intuicyjnie. Ok. Przejdźmy do wyglądu. Z tego kodu nie jestem 100% zadowolony więc jakby ktoś chciał pomóc w poprawieniu to zapraszam do napisania komentarza lub maila, na adres który jest w profilu. Zacznijmy od samego wyglądu. Robimy to w pliku Generic.xaml

<Setter Property=”Template”>
    <Setter.Value>
        <ControlTemplate TargetType=”{x:Type local:MenuButton}”>
            <Grid x:Name=”grid”>
                <Image Name=”DefaultImage” Source=”{TemplateBinding NormalStateImage,
                                           Converter={StaticResource StringToImage}}” />
                <Image Name=”HoverImage”   Source=”{TemplateBinding HoverStateImage,
                                           Converter={StaticResource StringToImage}}”
                                           Visibility=”Hidden” />                       
                <Image Name=”ClickImage”   Source=”{TemplateBinding PressedStateImage,
                                           Converter={StaticResource StringToImage}}”
                                           Visibility=”Hidden” />
                <Image Name=”DisabledImage” Source=”{TemplateBinding DisabledStateImage,
                                           Converter={StaticResource StringToImage}}”
                                           Visibility=”Hidden” />
            Grid>                   
        ControlTemplate>
    Setter.Value>
Setter>

4 obrazki. Tu jest minus tego rozwiązania. Odnośnie konwertera to jest to prosty – weź-łańcuch-znaków-i-zamień-na-bitmapę konwerter. Pominę go. Jeśli ktoś będzie go potrzebował niech napisze maila – podeślę.
Na koniec zostały nam same smaczki czyli zmiana stanów. W moim przypadku wyglądają one następująco:

<ControlTemplate.Triggers>
    <Trigger Property=”IsMouseOver” Value=”True”>
        <Setter TargetName=”ClickImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”DefaultImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”DisabledImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”HoverImage” Property=”Visibility” Value=”Visible” />
    Trigger>
    <Trigger Property=”IsPressed” Value=”True”>
        <Setter TargetName=”grid” Property=”RenderTransformOrigin” Value=”.5,.5″ />
        <Setter TargetName=”grid” Property=”RenderTransform”>
            <Setter.Value>
                <ScaleTransform ScaleX=”.9″ ScaleY=”.9″ />
            Setter.Value>
        Setter>
    Trigger>
    <Trigger Property=”IsEnabled” Value=”False”>
        <Setter TargetName=”ClickImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”DefaultImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”HoverImage” Property=”Visibility” Value=”Hidden” />
        <Setter TargetName=”DisabledImage” Property=”Visibility” Value=”Visible” />
    Trigger>
ControlTemplate.Triggers>

Jak widać cały trik polega na odpowiednim ukrywaniu i pokazywaniu obrazków. Nie jest to może bardzo wyszukane rozwiązanie ale działa. Dodatkowo w trybie Pressed zrezygnowałem z obrazka na rzecz skalowania grida. Ot i cała filozofia. Może komuś się przyda! Całość do ściągnięcia z http://plukasik.eu/files/MenuButton.zip
Uwaga: Dla czytających tylko RSS’a tego bloga. Zacząłem ćwierkać 🙂 – @pawel_lukasik. Można śledzić – zapraszam.

Zapraszamy na

Visual Studio 2010 Community Launch to cykl konferencji, które odbęda się w największych miastach Polski podczas których to zostaną zaprezentowane nowości z świeżo wydanego Visual Studio w wersji 2010 oraz najnowszego .NET Framework 4.

Już 24 kwietnia (sobota) w Instytucie Informatyki UWr przy ul. Juliot-Curie 15
odbędzie się pierwsze spotkanie z tego cyklu. Podczas 5 sesji tematycznych zostanie zaprezentowany bardzo szeroki wachlarz nowości, od ogólnego pokazania ‘co nowego’ poprzez najnowsze zmiany w języku C# a skończywszy na programowaniu równoleglym i IntelliTrace’ie.

Więcej informacji o wydarzeniu jak i agendę można znaleźć na stronie: http://ms-groups.pl/vs2010cl/Strony/VS2010CL_Wroclaw.aspx
Wsród uczestników spotkania podczas konferencji zostaną rozlosowane nagrody.

Wydarzenie jest bezpłatne lecz wymagana jest wczesniejsza rejestracja. Liczba miejsc jest ograniczona.

Serdecznie zapraszamy do udziału,
Wrocławska Grupa .NET

Dopisano: Plakat promujący wydarzenie w formacie DeepZoom – VS 2010 CL we Wrocławiu

Kolejny post ‘ku pamięci’, abym w przyszłości nie musiał tego szukać.

Bawiłem się ostatnio przyciskiem, który ma pokazywać ContextMenu po naciśnięciu przycisku. Kod wyglądał (mniej więcej) następująco:

<Button Content=”ContextMenu” Click=”Button_Click”>                           

    <Button.ContextMenu>

        <ContextMenu x:Name=”menu”>

            <MenuItem Header=”Item 1″ Command=”ApplicationCommands.Close” />

            <MenuItem Header=”Item 2″ Command=”ApplicationCommands.Open” />

        ContextMenu>

    Button.ContextMenu>

Button>

Jak widać wykorzystuję Commands. Automatyczne pojawianie się ContextMenu po przyciśnięciu prawego przycisku działało bez problemu, tak więc nie spodziewałem się problemów z kodem poniżej:

private void Button_Click(object sender, RoutedEventArgs e)

{

    menu.IsOpen = true;

}

Jednak ku mojemu zdziwieniu tak o to wyglądało menu:cm

I oczywiście CanExecute zawsze ustawiało e.CanExecute = true.

Co się zatem dzieje, że to nie działa? Okazuje się, że samo pokazanie nie sprawdza czy dane polecenie może być wykonane czy nie. Należy wykonać dodatkową operację już po pokazaniu menu. Zatem poprawnie należy to zrobić w następujący sposób:

private void Button_Click(object sender, RoutedEventArgs e)

{

    menu.IsOpen = true;

    CommandManager.InvalidateRequerySuggested();

}

Rozwiązanie, znalezione na MSDN – ContextMenu – Command and sharing across controls. Może komuś zaoszczędzi to trochę czasu.

Dopisano: Wygląda, że się trochę pośpieszyłem. Wywołanie tej instrukcji powoduje wykonanie CanExecute, tylko w jakichś szczególnych sytuacjach?
Ktoś wie jak temu zaradzić?

Wygląda na to, że działa danie zwykłego prostego Focus() w konstruktorze okna zaraz po InitializeComponents().

Ponownie nie jest mi dane pisać o TPL. Cóż ważne, że dane jest pisać o czymkolwiek technicznym. Dziś będzie o WPFie i dziwnym błędzie, który męczył mnie przez prawie dwa dni. Być może oszczędzi to komuś trudów. Zacznijmy zatem…
Aplikacją oczywiście w WPFie. Jako, że budowałem dla niej instalator postanowiłem sprawdzić czy wszystkie niezbędne komponenty są dołączane. Chciałem oczywiście, aby w przypadku braku .NET instalował się on skorzystałem z rozwiązania podanego przez Bartka Szafko we wpisie Instalowanie komponentów zależnych.
Mój bootstraper wyglądał tak:

   
      False
      .NET Framework 3.5
      true
   

   
      Windows Installer 3.1
   


Pomijając nieistotne elementy…
Instalator się ładnie budował, proces instalacji na czystej wirtualce XP przebiegał bez problemu ale po zainstalowaniu zamiast działającej aplikacji raczył mnie takim o to widokiem:
error
Ciutkę tajemniczę. Kernel32.dll? Hmm…problem potęgował fakt, że inna aplikacja w WPF działała bez najmniejszego zająknięcia. Próbowałem już chyba wszystkiego: wyrzucanie dll’ek (Enterprise Library, SharpZipLib, HtmlAgilityPack), które mogły powodować, że nie mam czegoś zainstalowanego na tej maszynie. Podglądanie pod reflektorem zależności i próba wymyślenia czego może tam brakować. Instalowanie multum pakietów z nadzieją, że może to rozwiąże problem. Nic nie pomagało…
Byłem praktycznie pokonany, ale dziś czytając o wydajności aplikacji w WPF i jak ją możemy zwięksyć natknąłem się na zadanie, iż w 3.5 SP1 dodano do WPF sporo usprawnień. Żółta lampka :). Wiedziałem, że SP1 dodaje trochę nowych rzeczy do WPFa i pomyślałem o tym, jakby się taki błąd gdy użyję czegoś z SP1 a mam zainstalowany tylko .NET 3.5 objawiał. Sprawdziłem i oczywiście mój bootstraper instalował tylko .NET 3.5 bez SP1. Szybko pobrałem SP1 i zainstalowałem, aby sprawdzić….no i udało się. Program wystartował. Eureka.
Oczywiście problemem był ContentStringFormat, który został dodany w SP1 (lub SP3 do .NET 3.0). W WiX’ie oczywiście miałem sprawdzanie zainstalowania tylko .NET 3.5 dlatego instalator działał poprawnie…
Głupi błąd, ale może ten wpis zaoszczędzi komuś cenny czas. Błąd ten jednak powinien być trochę mniej tajemniczy… teraz zostało tylko dołączyć .NET 3.5 SP1 jako Prerequsite z czym też jest trochę problemów, ale przynajmniej wiadomo co robić.

WPF

Wracamy do kursu o TPL. Dziś trochę informacji faktów o .For oraz .ForEach.
Te dwie metody dają podstawowy sposób na zrównoleglenie naszego kodu bez większego wysiłku

Parallel.For(lowerBound, exclusiveUpperBound, () => { /*action*/});

Parallel.ForEach<int>(Enumerable.Range(1, 100), (int item) => { /*body*/});

Tak więc prościej już by się nie dało :). Jednak funkcje te mają możliwość przyjmowania dodatkowych parametrów.

ParallelOptions

Za pomocą tego parametru możemy ustawić kilka dodatkowych właściwości takich jak:

  • maksymalny stopień paralelizacji (MaxDegreeOfParallelism)
  • token anulacji (CancellationToken)
  • własny scheduler zadań (TaskScheduler)

Zakończenie pętli

Sytuacja, gdy pętla zakończy się powodzeniem jest jak najbardziej przez nas pożądana, ale oczywiście trzeba przygotować się na sytuację, gdy coś się nie uda lub pętla zakończy się przedwcześnie. Aby sprawdzić, jak zakończyła się pętla należy sprawdzić zawartość zmiennej zwracanej przez Parallel.For – zwracany jest obiekt ParallelLoopResult.

Stop
Parallel.For(1, 1000, (int i, ParallelLoopState state) =>
{
/*action*/
if (found)
{
state.Stop();
}
});

Warto zauważyć inną definicję ciała pętli. Jeśli chcemy skorzystać z możliwości zakończenia pętli przedwcześnie musimy wykorzystać definicję przyjmującą jako argumenty indeks oraz typ ParallelLoopState. Po wywołaniu metody Stop, kolejne iteracje nie będą uruchamiane. Jeśli chcemy przerwać już uruchomione iteracje możemy sprawdzić właściwość IsStopped i odpowiednio na nią reagować.

Break

Break działa podobnie do Stop ale daje dodatkowe gwarancje. Jeśli Break zostanie wywołane z 50 iteracji, spowoduje zatrzymanie wywoływania iteracji powyżej 50-tej, ale iteracje 0-49 nadal będą wywołane. Break może zostać wywołane wielokrotnie a najniższa iteracja, z której została wywołana metoda zostaje zapisana we właściwości: LowestBreakIteration.

Stop i Break nie mogą być używane równocześnie.

Wyjątki

Czasem mamy sytuację, iż nasze ciało pętli rzuca wyjątkiem. Jak sobie radzić w takiej sytuacji. TPL wprowadza nowy typ wyjątku: AggregateException. Nie różni się to od zwykłego wyjątku, z tym, że umożliwia zapisanie wiele wyjątków w polu InnerException. Aby sprawdzić czy w jakiejś iteracji nie wystąpił wyjątek należy sprawdzić właściwość IsExceptional na obiekcie ParallelLoopState.

Na dziś to tyle. Zachęcam do zabawy TPL w .NET 4.0 naprawdę przydatna sprawa.

Dzisiejszy wpis bardziej dla mnie na przyszłość ale może ktoś skorzysta.

Ostatnio miałem potrzebę użycia takiej wartości StringFormat, aby jego wartość zaczynała się od spacji. Każdy kto kiedykolwiek bawił się tą właściwością wie, że takie coś jak poniżej nie spełni swojej roli.

<TextBlock FontSize=”16″ Text=”{Binding Value, Mode=OneWay, StringFormat= ({0}) dB}” x:Name=”test” />

Zauważcie spację pomiędzy znakiem = a ( w parametrze StringFormat. Wiem, że przykład może naciągany, ale w moim przypadku taki coś było konieczne.

Wynik jest jednak taki jak poniżej.

image

Jak zatem możemy uzyskać prowadzącą spację (leading space po polsku)? StringFormat obsługuje specjalną parę znaków {}. Wystarczy nasz StringFormat rozpocząć od tych znaków a nasza spacja będzie uwzględniona

<TextBlock FontSize=”16″ Text=”{Binding Value, Mode=OneWay, StringFormat={} ({0}) dB}” x:Name=”test” />

image

Tę samą sztuczkę musimy zastosować jeśli nasz StringFormat chcemy rozpocząć znakiem {.

W dokumentacji MSDN znak ten pojawia się w przykładzie, ale nie ma nic na jego temat – BindingBase.StringFormat property.

Jako, że w tej chwili jestem trochę uziemiony odnośnie pisania kolejnych wpisów o TPL (ale spokojnie powrócę do nich) postanowiłem napisać o czymś innym. Ale po kolei…

Wszystko zaczęło się od pytania na portalu devpytania.pl. Zacząłem się zastanawiać czy rzeczywiście nie ma jakiejś metody na to. Fullscreen’a, aż tak często nie używam, ale czasem się zdarza i rzeczywiście zachowanie to jest denerwujące. Tak więc postanowiłem coś z tym zrobić i napisać małego AddIn’a do VS, który będzie za to odpowiedzialny.

Zatem do działa.

Nie będę się rozpisywał co trzeba kliknąć w VS, aby stworzyć projekt AddIn’a bo z tym nawet najbardziej początkujący sobie poradzą. Zaczynamy zatem od momentu jak już nasze pliki zostaną utworzone. To co musimy zrobić to dowiedzieć się kiedy aplikacja jest w trybie Debug a kiedy nie, czy jesteśmy uruchomieni w trybie pełnoekranowym oraz to jak się przełączać w pomiędzy normalnym oknem a tym trybem.

Tryb pracy

Musimy rozeznać się w jakim trybie pracy aktualnie się znajdujemy a konkretniej musimy być powiadamiani przy zmianie tego trybu z Debugowania na Normalny. Na szczęście API pluginów dostarcza nam eventy. Jednym z nich jest ModeChanged. Jak się do nich podpiąć?

  _events = _applicationObject.Events.DTEEvents;
_events.ModeChanged += DTEEvents_ModeChanged;

Od razu uwaga. Uzywać należy naszej zmiennej w klasie przy podłączaniu się do eventów. Taki kod jak poniżej nie zadziałą prawidłowo
(ale nie będzie też rzycać wyjątków. Będzie wyglądało tak jakby eventy nie przychodziły).

_applicationObject.Events.DTEEvents.ModeChanged += DTEEvents_ModeChanged; // Nie zadziała!!!

Po podłączeniu się do eventów będziemy otrzymywać powiadomienia o zmianie wraz z parametrem LastState (będącym enumeracją vsIDEMode). Zatem możemy już działać.

Komunikaty na zewnątrz

Krótka przerwa przed kontynuacją. Choć AddIn’y możemy spokojnie debugować to czasem warto mieć możliwość wypisania czegoś na ekran. Tu przychodzi z pomocą konsola OutputWindow. Możemy dodawać do niej nasze “zakładki” i na nich pisać. Jak to zrobić pokazuje kod poniżej:

Windows w = _applicationObject.Windows;            
Window ww = w.Item(Constants.vsWindowKindOutput);
OutputWindow outputWindow = (OutputWindow)ww.Object;
outputPane = outputWindow.OutputWindowPanes.Add("Octal Solutions - Fullscreen & Debug");

Ostatnia instrukcja daje nam obiekt, na którym możemy wywołać metodę OutputString i nasz tekst ukaże się na naszej zakładce w oknie Output.
Uzbrojeni w taką wiedzę możemy przejść dalej.

Tryb pełnoekranowy

Z tym jest najwięcej problemów, bo nie udało się znaleźć właściwości czy metody zawracającej tę wartość. Trzeba sprawdzić rozmiar głównego okna VS:

private bool IsFullScreen()
{
System.Drawing.Rectangle screen = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
var mainWindow = _applicationObject.DTE.MainWindow;
return mainWindow.Width == screen.Width && mainWindow.Height == screen.Height;
}

Zmiana trybu pracy

Chyba najprostsza z tych rzeczy. Wystarczy wykonać polecenie View.Fullscreen. Visual Studio przełącza się pomiędzy pełnym ekranem a normalnym oknem.

_applicationObject.ExecuteCommand("View.Fullscreen", "");

W zasadzie to już wszystko co musimy zrobić aby nasz AddIn zadziałał. Warto pamiętać jeszcze o odłączeniu się od zdarzeń, gdy zostaje on wyładowane z pamięci tak by zostawić po sobie porządek.

Pełny plugin można ściągnąć używając poniższego linka: Fullscreen & Debug. Jeśli masz jakieś komentarze, uwagi napisz do mnie lub zostaw komentarz pod postem.

Tę serię przede wszystkim należy zacząć od pytania – po co nam to? To z kolei możemy rozbić na dwa kolejne. Po co nam w ogóle programowanie równoległe oraz po co nam TPL – przecież już mamy dostępne narzędzia.

Po co nam to w ogóle

Mam nadzieję, iż tak na prawdę na to pytanie odpowiadać nie trzeba, ale dla porządku zróbmy to. Potrzebujemy bo prawo Moore’a przestaje działać. Nie możemy już powiedzieć naszemu klientowi, który narzeka na słabą wydajność aplikacji, że gdy będziemy wydawać to oprogramowanie to procesory będą dwukrotnie szybsze więc nie będzie problemu (pytanie: czy ktoś kiedykolwiek tak powiedział?? :)). Dodatkowo procesory teraz posiadają 2 lub więcej rdzeni więc tak na prawdę wykorzystujemy tylko cząstkę dostępnej nam mocy. Przez to nasze aplikacje pracują mniej wydajniej i obciążając procesor blokują inne.

Po co nam TPL

No dobrze. Nawet jeśli widzimy potrzebę wykorzystania drzemiącej mocy naszych procesorów, to po cóż nam coś nowego? Przecież mamy namespace System.Threading. Po cóż więc nam coś nowego – jakieś Taski i inne takie? W zasadzie – po nic. Problem z System.Threading jest jednak taki, że API tam dostępne jest skomplikowane i przez to nie jest wykorzystywane w wielu przypadkach. Zrównoleglenie kodu zawsze zostawiamy na potem, aż w końcu po release’ie stwierdzamy, że znów zapomnieliśmy o nim – no bo w końcu wydajność to nie jest nasz priorytet.

TPL wprowadza nową jakość do wątków – udostępnia je – jak to powiedział Scott Hanselman – dla mas. Dzięki nowemu i prostszemu API więcej osób w łatwy sposób będzie mogło zrównoleglić kod w swojej aplikacji. Jednak nie dajcie się zwieść – to, że API jest łatwe, ładne i przyjemne nie oznacza, że nie można za jego pomocą zrobić sobie krzywdy. Można! Jednak jeśli będziemy uważni, możemy dużo więcej osiągnąć.

Na dziś tyle. Zainteresowani tematem znajdą dużo informacji w linku poniżej – a już niedługo przestanę teoretyzować i zaczniemy bawić się naszym nowym API.

Link: Parallel Computing