Top Banner
Projektowanie komponentów wizualnych WOJCIECH
47

Projektowanie komponentów wizualnych

Jul 17, 2015

Download

Software

PGS_Software
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Projektowanie komponentów wizualnych

Projektowanie komponentów wizualnychWOJCIECH

Page 2: Projektowanie komponentów wizualnych

Agenda

Czym są kontrolki niestandardowe?

Case study

Rodzaje komponentów

Elementy WinAPI

Architektura i implementacja prostych kontrolek

Podsumowanie

Page 3: Projektowanie komponentów wizualnych

Kilka faktów

Konkretne rozwiązania

Programowanie (relatywnie) niskopoziomowe

Pomysły są autorskie

Page 4: Projektowanie komponentów wizualnych

Czym są komponenty niestandardowe?

Kontrolki standardowe

Page 5: Projektowanie komponentów wizualnych

Czym są komponenty niestandardowe?

Kontrolki niestandardowe

Page 6: Projektowanie komponentów wizualnych

Od zera? Dlaczego?Ścisłe wymagania (internetowe kontrolki są zwykle generyczne)Problem: budżet (często $1000+ za stanowisko)Brak kontroli nad źródłemProjektowanie architektury (kierunek rozwoju)Alternatywa: internetowe repozytoria lub Open Source

Page 7: Projektowanie komponentów wizualnych

Kiedy nie pisać osobnych kontrolek?

Czy naprawdę jest potrzebna?Gotowa kontrolka już istniejePodobna kontrolka już istniejeSpecjalny wygląd aplikacji (z wyjątkami)

Page 8: Projektowanie komponentów wizualnych

Case study

Page 9: Projektowanie komponentów wizualnych

Case study: SpkToolbar- Brak analogicznego (darmowego) rozwiązania

- Bardziej kontrolowane rozwiązanie niż oryginalny Ribbon dla Delphi

- Obecnie OpenSource na LCCR (Lazarus Code & Components Repository)- Używany w aplikacjach OpenSource (link)

Page 10: Projektowanie komponentów wizualnych

Case study: ProCalc

- Ścisłe powiązanie z architekturą aplikacji

- Renderowanie przy pomocy Direct2D i Direct3D

Page 11: Projektowanie komponentów wizualnych

Case study: ProTranscriber

- Brak darmowego odpowiednika- Wymagana specyficzna funkcjonalność- Powiązanie z architekturą aplikacji

Page 12: Projektowanie komponentów wizualnych

Case study: Virtual treeview

- Brak wymaganej funkcjonalności w standardowym komponencie

- Brak tego typu komponentu w Internecie (tylko naśladujące)

- Dostępny jako Open Source na Codeplex (hasło: Spk.Controls)

Page 13: Projektowanie komponentów wizualnych

Case study: NodeLab

- Bardzo specyficzne wymagania

- Powiązanie z architekturą aplikacji

Page 14: Projektowanie komponentów wizualnych

Rodzaje komponentów

Page 15: Projektowanie komponentów wizualnych

Rodzaje komponentówCustom controls – kontrolki „od zera”User controls – kontrolki kompozytowe

- Reużywalne fragmenty aplikacji- Złożone z innych kontrolek

- Zawierają pewną logikę

- Odcinają od wewnętrznej struktury- Ograniczona funkcjonalność

- Projektowane dla ściśle konkretnego celu

- Implementowane w całości przez programistę

- Zawierają dużo wewnętrznej logiki (związanej z wyświetlaniem i interakcją z użytkownikiem)

Page 16: Projektowanie komponentów wizualnych

Rodzaje komponentówWyjątek - WPF

<UserControl x:Name="rootControl"x:Class="Webnodes.OntologyDesigner.Common.Controls.BackControl"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:vs="clr-namespace:Microsoft.VisualStudio.Shell..."mc:Ignorable="d">

<Button Width="24" Height="24" Command="{Binding ElementName=rootControl, Path=BackCommand}"><Button.Template>

<ControlTemplate TargetType="{x:Type Button}"><Viewbox Stretch="Uniform">

<Canvas Width="16" Height="16"><Path x:Name="Background" Fill="Transparent">

<Path.Data><PathGeometry Figures="M 16 8 A 8 8 ..." FillRule="NonZero"/>

</Path.Data></Path><Path x:Name="Circle" Fill="{DynamicResource GrayIconColor}">

<Path.Data><PathGeometry Figures="M 8 0 C 3.581722..." FillRule="NonZero"/>

</Path.Data></Path><Path x:Name="Arrow" Fill="{DynamicResource GrayIconColor}">

<Path.Data><PathGeometry Figures="m 3 8 4 -4 3 0 ..." FillRule="nonzero"/>

</Path.Data></Path>

</Canvas></Viewbox>

<ControlTemplate.Triggers><Trigger Property="IsMouseOver" Value="True">

<Setter TargetName="Background" Property="Fill"Value="{DynamicResource {x:Static vs:VsBrushes.HighlightKey}}" />

<Setter TargetName="Circle" Property="Fill"Value="Transparent" />

<Setter TargetName="Arrow" Property="Fill"Value="{DynamicResource {x:Static vs:VsBrushes.ButtonFaceKey}}" />

</Trigger></ControlTemplate.Triggers>

</ControlTemplate></Button.Template>

</Button></UserControl>

Page 17: Projektowanie komponentów wizualnych

Komponent kompozytowy

Page 18: Projektowanie komponentów wizualnych

Komponent kompozytowy

Jednolity wygląd aplikacji (i konsekwencje)

Dodatkowa funkcjonalność (walidacja)

Wygodny interface użytkownikaWygodne API

Page 19: Projektowanie komponentów wizualnych

Architektura - API

Odcinamy użytkownika od wewnętrznych komponentów

Publikujemy własności i zdarzenia wynikające z przeznaczenia komponentu

public string Username { get; set; }

public string Password { get; set; }

[Browsable(false)]

public bool UsernameValid { get; }

[Browsable(false)]

public bool PasswordValid { get; }

public event EventHandler UserValidChanged;

public event EventHandler PasswordValidChanged;

Page 20: Projektowanie komponentów wizualnych

Architektura – zdarzeniaChronione, wirtualne triggery zdarzeń

protected virtual void OnUserValidChanged(){

if (UserValidChanged != null)UserValidChanged(this, EventArgs.Empty);

}

private void SetUserValid(bool value){

(...)

if (changed)OnUserValidChanged();

}

public event EventHandler UserValidChanged;

Page 21: Projektowanie komponentów wizualnych

Architektura – zdarzenia- Modyfikacja oryginalnej funkcjonalności (przed, po, zamiast)

- Decyzja o zgłaszaniu zdarzenia

- Ułatwia rozwój kontrolki (zwiększa elastyczność)

protected override void OnResize(EventArgs e){

if (Height != 60)Height = 60;

base.OnResize(e); }

Page 22: Projektowanie komponentów wizualnych

Użycie komponentu

private void CheckCanConfirm(){

bOK.Enabled = pbPassword.UsernameValid && pbPassword.PasswordValid;}

private void HandleValidChanged(object sender, EventArgs e){

CheckCanConfirm();}

public MainForm(){

InitializeComponent();CheckCanConfirm();

}

Page 23: Projektowanie komponentów wizualnych

Komponent niestandardowy

Page 24: Projektowanie komponentów wizualnych

Komponent niestandardowy- Start small: przycisk

- Standardowe zachowanie – obserwacja standardowego przycisku

- Wygląd - TODO

Page 25: Projektowanie komponentów wizualnych

Podstawy WinAPI

(Square Enix)

Page 26: Projektowanie komponentów wizualnych

Podstawy WinAPI

Okno

Okno

Okno

OknoOkno

Okno

Okno

Okno Okno

Okno

Okno

Page 27: Projektowanie komponentów wizualnych

Podstawy WinAPI

Window

OK

Handle

Handle

• Size• Style• Icon, small icon• Cursor• Background• MenuName• Text

Page 28: Projektowanie komponentów wizualnych

Dygresja - WPF

Window

OK

Handle

Page 29: Projektowanie komponentów wizualnych

Podstawy WinAPI - zdarzenia

WindowsUżytkownikMouseMove Kolejka

komunikatów

WM_MouseMove

512

WndProc(implementacja .NET WinForms)

GetMessage()

OnMouseMoveMouseMove

Użytkownik System operacyjny

.NET FrameworkProgram

OnMouseMove

Page 30: Projektowanie komponentów wizualnych

Podstawy WinAPI - kolejka komunikatów

WM_SHOW

WM_MOUSEDOWN

WM_MOUSEUP

WM_MOUSEMOVE

WM_MOUSEMOVE

WM_MOUSEDOWN

(System operacyjny)

Wciśnięcie przycisku myszy

Puszczenie przycisku myszy

Przesunięcie myszy

Przesunięcie myszy

Wciśnięcie przycisku myszy

Page 31: Projektowanie komponentów wizualnych

Podstawy WinAPI - responsywność

Aplikacja tworząca okno na pulpicie wchodzi w kontrakt z DWMem –zobowiązuje się do płynnego przetwarzania komunikatów

Jeśli żaden komunikat nie został zdjęty z kolejki w przeciągu 5 sekund, okno jest oznaczone jako zawieszone

Wniosek: musimy dbać o przetwarzanie komunikatów w możliwie jak najkrótszym czasie.

Page 32: Projektowanie komponentów wizualnych

Podstawy WinAPI – WM_PAINT

WM_SHOW

WM_MOUSEDOWN

WM_MOUSEUP

WM_PAINT

WM_MOUSEMOVE

WM_MOUSEDOWN

(System operacyjny)

Wciśnięcie przycisku myszy

Puszczenie przycisku myszy

Invalidate()

Przesunięcie myszy

Wciśnięcie przycisku myszy

Page 33: Projektowanie komponentów wizualnych

Architektura – WinAPI - wnioski

Komunikaty muszą być przetwarzane na bieżąco – najlepiej natychmiast

Wszystkie czasochłonne operacje muszą zostać przesunięte do osobnego wątku

Możemy przechwytywać niskopoziomowe komunikaty w WndProc◦ Ale wszystkie najważniejsze są obsłużone w WinFormsach

Możemy wysyłać komunikaty w celu osiągnięcia konkretnego efektu

Możemy bezkarnie wołać Invalidate() bez wpływu na wydajność◦ Stąd wniosek: czasochłonne (synchroniczne) operacje warto realizować leniwie

Page 34: Projektowanie komponentów wizualnych

Architektura komponentu

Page 35: Projektowanie komponentów wizualnych

Pomysł na architekturę: mysia maszyna stanu

Idle

Pressed Pressed - canceledHoverMouseDown

MouseUp

MouseLeave

MouseEnter

MouseUpMouseEnterprivate enum MouseMode{

Idle,Hover,Pressed,PressedCanceled

}

Page 36: Projektowanie komponentów wizualnych

Implementacja

Page 37: Projektowanie komponentów wizualnych

Implementacja - OnMouseEnter

protected override void OnMouseEnter(EventArgs e){

if (mouseMode == MouseMode.Idle){

mouseMode = MouseMode.Hover;Invalidate();

}

base.OnMouseEnter(e);}

Page 38: Projektowanie komponentów wizualnych

Implementacja - OnMouseLeave

protected override void OnMouseLeave(EventArgs e){

if (mouseMode == MouseMode.Hover){

mouseMode = MouseMode.Idle;Invalidate();

}

base.OnMouseLeave(e);}

Page 39: Projektowanie komponentów wizualnych

Implementacja - OnMouseDown

protected override void OnMouseDown(MouseEventArgs e){

if (mouseMode == MouseMode.Hover){

if (e.Button == MouseButtons.Left){

mouseMode = MouseMode.Pressed;

Invalidate();}

}

base.OnMouseDown(e);}

Page 40: Projektowanie komponentów wizualnych

Implementacja - OnMouseUpprotected override void OnMouseUp(MouseEventArgs e){

if (mouseMode == MouseMode.Pressed){

mouseMode = MouseMode.Hover;

Invalidate();

OnButtonClick();}else if (mouseMode == MouseMode.PressedCanceled){

mouseMode = MouseMode.Idle;

Invalidate();}

base.OnMouseUp(e);}

Page 41: Projektowanie komponentów wizualnych

Implementacja - OnMouseMoveprotected override void OnMouseMove(MouseEventArgs e){

if (mouseMode == MouseMode.Pressed){

if (!this.ClientRectangle.Contains(e.Location)){

mouseMode = MouseMode.PressedCanceled;

Invalidate();}

}else if (mouseMode == MouseMode.PressedCanceled){

if (this.ClientRectangle.Contains(e.Location)){

mouseMode = MouseMode.Pressed;

Invalidate();}

}

base.OnMouseMove(e);}

Page 42: Projektowanie komponentów wizualnych

Implementacja - ctor

public MyButton()

{

this.ResizeRedraw = true;

this.DoubleBuffered = true;

mouseMode = MouseMode.Idle;

}

Page 43: Projektowanie komponentów wizualnych

Implementacja - doublebufferingBez doublebufferingu

Hover

Ramka

Tło

Tekst

Z doublebufferingiem

Hover

Ramka

Tło

Tekst

Hover Render

Page 44: Projektowanie komponentów wizualnych

Live demo

Page 45: Projektowanie komponentów wizualnych

Co zapamiętać?Piszmy kontrolki, gdy są naprawdę potrzebne

Kompozytowa czy niestandardowa?

Maksymalnie wygodny interface (zarówno graficzny jak API)

Kolejka komunikatów WinAPI

Szybka obsługa komunikatów (reponsywność)

Długie obliczenia: synchroniczne – leniwie; asynchroniczne – do osobnego wątku

Komponent - mysia maszyna stanu

Doublebuffering

Page 46: Projektowanie komponentów wizualnych

Co następnym razem?

- Obszar roboczy

- Matematyka: układy współrzędnych

- Metryki

Page 47: Projektowanie komponentów wizualnych

Pytania

?