Grid.Row="0" Grid.Column="0"
Transkrypt
Grid.Row="0" Grid.Column="0"
Windows Presentation Foundation – WPF (1) Programowanie Wizualne Paweł Wojciechowski Instytut Informatyki, Politechniki Poznańskiej 2012 Architektura Managed WPF API PresentationFramework.dll PresentationCore.dll WindowsBase.dll milcore.dll WindowsCodecs.dll Direct3D User32 Media Integration Layer Hierarchia klas object DispatcherObject DependencyObject Visual UIElement FrameworkElement Control realizacja modelu STA (single thread affinity) model właściwości – cechy: informowanie o zmianach, dziedziczenie wartości domyślnej obiekt, który jest „rysowany” – transformacje, przycinanie, przezroczystość, hit tests layout, inputs, events, data binding, animation, styles kluczowe właściwości obiektów obiekty interaktywne. Właściwości: font, foreground, background colors Jednostki wielkości w WPF (1) jednostki wielkości uwzględniają DPI ekranu zaleta: na wszystkich ekranach wielkość elementu jest taki sam wada: ... 𝑙𝑖𝑐𝑧𝑏𝑎 𝑝𝑖𝑘𝑠𝑒𝑙𝑖 𝑛𝑎 𝑗𝑒𝑑𝑛𝑜𝑠𝑡𝑘ę 𝑊𝑃𝐹 = 𝑟𝑜𝑧𝑚𝑖𝑎𝑟 𝑒𝑙𝑒𝑚𝑒𝑛𝑡𝑢 𝑤 𝑝𝑖𝑘𝑠𝑒𝑙𝑎𝑐ℎ = 𝑟𝑜𝑧𝑚𝑖𝑎𝑟 𝑠𝑡𝑎ł𝑒𝑗 𝑗𝑒𝑑𝑛𝑜𝑠𝑡𝑘𝑖 𝑊𝑃𝐹 𝐷𝑃𝐼 𝑚𝑜𝑛𝑖𝑡𝑜𝑟𝑎 𝑙𝑖𝑐𝑧𝑏𝑎 𝑝𝑖𝑘𝑠𝑒𝑙𝑖 𝑛𝑎 𝑗𝑒𝑑𝑛𝑜𝑠𝑡𝑘ę 𝑊𝑃𝐹 𝑀𝑜𝑗𝐸𝑙𝑒𝑚𝑒𝑛𝑡. 𝐴𝑐𝑡𝑢𝑎𝑙𝑊𝑖𝑑𝑡ℎ Jednostka WPF = 1/96 Gdzie w systemie Windows ustawia się DPI? Jednostki wielkości w WPF (2) WPF główne cechy wspomaganie sprzętowe wykorzystanie Direct3D niezależność od rozdzielczości wykorzystanie systemu DPI i ustawień systemowych brak ustalonego wyglądu kontrolek kontrolki są „lookless” – są w pełni konfigurowalne deklarowane interfejsy użytkownika interfejs użytkownika deklarowany jest w specjalnym języku XAML XAML XAML Extensible Application Markup Language case sensitive główny cel – oddzielenie logiki aplikacji od jej wyglądu XAML nie jest niezbędny do działania WPFa Kompilacja -> BAML (Binary Application Markup Language) Podstawowe właściwości: Każdy element zdefiniowany w XAMLu odpowiada klasie w .NET. Jak w każdym dokumencie XML jeden element można osadzać w innym Właściwości klas mogą zostać ustawione poprzez atrybuty. Pierwsza aplikacja Plik xaml: <Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="350" Width="525"> <Grid> </Grid> </Window> Plik cs: namespace WpfApplication1 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } } } Przestrzenie nazw „Namiary” na lokację klas .NET – atrybut xmlns xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" domyślny pakiet w aplikacji zawierający wszystkie klasy WPF. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" pakiet zawierający różne narzędzia umożliwiające wpłynięcie na sposób interpretacji dokumentu XAML Odwołanie do własnych klas xmlns:prefix="clr-namespace:Namespace;assembly=AssemblyName" <Page x:Class="WPFApplication1.MainPage" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:custom="clr-namespace:Sample;assembly=SampleLibrary"> ... <custom:ExampleClass/> ... </Page> Nazywanie elementów interfejsu Nadawanie nazw elementom nie jest obowiązkowe. <Grid> ... </Grid> Wymagane jest jednak jeśli wymagane są zmiany parametrów elementu kontrolnego w kodzie programu <Grid Name="Grid_1"> ... </Grid> Dla tak zdefiniowanej nazwy zostanie wygenerowana automatycznie następująca część kodu klasy MainWindow #line 5 "..\..\..\MainWindow.xaml" [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] internal System.Windows.Controls.Grid Grid_1; Właściwości komponentów proste typy i konwertery <Image Height="250" HorizontalAlignment="Left" Name="image1" Stretch="None" VerticalAlignment="Top" Width="250" Source="/WpfApplication1;component/Images/bitmap.jpg" /> Dla powyższego komponentu (Image) muszą być zdefiniowane następujące właściwości: Height, HorizontalAlignment, Stretch, VerticalAlignment, Width, Soruce Typy proste – prosta konwersja. Typy złożone np. właściwość Background wymaga specjalnego konwertera. Zaleca się nadawania wartości parametrów w XAMLu nie w kodzie aplikacji, ze względu na to, iż XAML jest parsowany i sprawdzany w czasie kompilacji, dzięki czemu wcześniej można wykryć potencjalne błędy. Właściwości komponentów właściwości złożone niektóre właściwości mogą być obiektami – problem z kowersją w XAML-u można definiować elementy zagnieżdżone w formie Rodzic.NazwaWłaściwości <Image Height="250" HorizontalAlignment="Left" Name="image1" Stretch="None" VerticalAlignment="Top" Width="250"/> <Image Name="image1" Stretch="None" VerticalAlignment="Top" Width="250"> <Image.Height>250</Image.Height> <Image.HorizontalAlignment>Left</Image.HorizontalAlignment> </Image> Właściwości komponentów właściwości złożone (2) <Grid Name="Grid_1"> <Grid.Background> <RadialGradientBrush> <GradientStop Offset="0.00" Color="White" /> <GradientStop Offset="0.5" Color="Red"/> <GradientStop Offset="0.75" Color="Indigo"/> <GradientStop Offset="1" Color="Violet"/> </RadialGradientBrush> </Grid.Background> </Grid> RadialGradientBrush rgb = new RadialGradientBrush(); GradientStop gradientStop1 = new GradientStop(); gradientStop1.Color = Colors.White; gradientStop1.Offset = 0; rgb.GradientStops.Add(gradientStop1); GradientStop gradientStop2 = new GradientStop(); gradientStop2.Color = Colors.Red; gradientStop2.Offset = 0.5; rgb.GradientStops.Add(gradientStop2); GradientStop gradientStop3 = new GradientStop(); gradientStop3.Color = Colors.Indigo; gradientStop3.Offset = 0.75; rgb.GradientStops.Add(gradientStop3); GradientStop gradientStop4 = new GradientStop(); gradientStop4.Color = Colors.Violet; gradientStop4.Offset = 1; rgb.GradientStops.Add(gradientStop4); Grid_1.Background = rgb; Właściwości komponentów Dynamiczne wartości właściwości w poprzednich przykładach wartości właściwości były statyczne Specjalna składnia umożliwia nadawanie wartości dynamicznych przez wiązanie jej z wartościami właściwości klas <Label Name="label1" Content="Hello World" Background="{x:Static SystemColors.HighlightColor}"/> wszystkie rozszerzone znaczniki (markup extensions) są zaimplementowane w klasach dziedziczących po System.Windows.Markup.MarkupExtension label1.Background = new SolidColorBrush(SystemColors.HighlightColor); <Label Name="label1" Content="Hello World"> <Label.Background> <SolidColorBrush> <SolidColorBrush.Color> <x:Static Member="SystemColors.HighlightColor"></x:Static> </SolidColorBrush.Color> </SolidColorBrush> </Label.Background> </Label> Właściwości komponentów właściwości powiązane Attached Properties właściwości zdefiniowane w jednym z komponentów, ale zdefiniowane w innej klasie używanie poprzez: TypDefniniujący.NazwaWłaściwości <Label Name="label1" Content="Hello World" Grid.Row="0" Grid.Column="0"/> są one przekształcane w wywołania metod TypDefiniujący.SetNazwaWłaściwości Grid.SetColumn(label1, 1); Elementy zagnieżdżone element musi zadecydować w jaki sposób obsługiwać elementy zagnieżdżone. Dzieje się to za pomocą jednego z trzech mechanizmów: Jeżeli rodzic implementuje interfejs IList, wywoływana jest metoda IList.Add() Jeżeli rodzic implementuje IDictionary, wywoływana jest metoda IDictionary.Add() elementy muszą mieć nadaną wartość parametru x:Key Jeżeli rodzicowi nadano atrybut ContentProperty, dziecko używane jest do nadania wartości wskazanej właściwości. <Button> Ala ma kota </Button> Zdarzenia w XAMLu można również definiować zdarzenia – NazwaZdarzenia="NazwaMetodyObsługiZdarzenia" <Button Content="Button" Name="button1" Click="button1_Click" /> private void button1_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Ala ma kota"); } Ładowanie i kompilacja XAML Implementacja aplikacji w WPFie może być realizowana za pomocą następujących metod: tylko kodu – definicja całego interfejsu za pomocą kodu np. w C# Kodu i plików XAML – podejście stosowane w przypadku dynamicznie zmieniających się interfejsów. Plik XAML jest ładowany podczas uruchomienia programu za pomocą klasy XamlReader z przestrzeni nazw System.Windows.Markup Kodu i plików BAML – najczęściej stosowane podejście. Interfejs opisany jest w plikach XAML, które są kompilowane do BAML i osadzane w pakiecie. Rozmieszczenie komponentów Layouts Filozofia rozmieszczania komponentów odejście od sztywnych ustawień pozycji i rozmiaru okno WPF może zawierać tylko jeden komponent zasady budowania interfejsów: elementy kontrolne nie powinny mieć ustawionego rozmiaru – powinny one wypełniać pojemnik w którym się znajdują można ograniczać minimalny i maksymalny rozmiar komponentów położenie elementów nie jest opisane współrzędnymi ekranu, ale rozmiarem, rozkładem itp. pojemnika(ów) w których się znajdują. Przerwy pomiędzy komponentami narzuca się poprzez wartości parametru Margin. pojemniki dzielą swoją przestrzeń pomiędzy swoje dzieci pojemniki mogą być zagnieżdżone Podstawowe rodzaje pojemników StackPanel WrapPanel DockPanel Grid UniformGrid Canvas TabPanel, ToolbarPanel, ToolbarOverflowPanel, VirtualizingStackPanel, InkCanvas Layout - właściwości HorizontalAlignment: Center, Left, Right, Stretch VerticalAlignment: Center, Top, Bottom, Stretch Margin MinWidth/MinHeight MaxWidth/MaxHeight Width/Height Właściwość Background musi zostać ustawiona (domyślnie null) jeśli pojemnik ma otrzymywać zdarzenia myszy! Naturalnie może to być kolor transparentny. StackPanel <StackPanel> <Label>Stack Panel</Label> <Button>Button 1</Button> <Button>Button 2</Button> <TextBox>TextBox 1</TextBox> <Button>Button 3</Button> <Button>Button 4</Button> </StackPanel> <StackPanel Orientation="Horizontal"> ... </StackPanel> StackPanel (2) <StackPanel Margin="3" Background="Aqua"> <Label HorizontalAlignment="Center" Margin="3">Stack Panel</Label> <Button HorizontalAlignment="Left" Margin="3">Button 1</Button> <Button HorizontalAlignment="Right" Margin="3">Button 2</Button> <TextBox Margin="3">TextBox 1</TextBox> <Button HorizontalAlignment="Center" Margin="3">Button 3</Button> <Button Margin="3">Button 4</Button> </StackPanel> Wyznaczanie wielkości komponentów Rozmieszczenie elementów pojemnika składa się z dwóch etapów: pomiaru (measure stage) – pojemnik „pyta” swoje dzieci o ich preferowany rozmiar rozmieszczenia (arrange stage) – dzieci są rozmieszczane w pojemniku na właściwych pozycjach Pojemniki nie wspierają przewijania (scrolls). Jest za to komponent – ScrollViewer. Informacje wykorzystywane przy określaniu rozmiaru komponentu to: Minimalny rozmiar – rozmiar komponentu będzie co najmniej taki, jak jego minimalny rozmiar Maksymalny rozmiar – rozmiar komponentu będzie mniejszy od jego maksymalnego rozmiaru (chyba, że minSize > maxSize) Zawartość – jeśli zawartość komponentu będzie wymagała większego rozmiaru, to zostanie on powiększony (DesiredSize) Wielkość pojemnika – jeżeli wielkość pojemnika jest mniejsza niż wielkość komponentu, to część komponentu zostanie obcięta (Horizontal|Vertical)Alignment – pojemnik zwiększy rozmiar komponentu jeśli dla tej własności zostanie ustawiona wartość Stretch Rozmiar komponentu może zostać odczytany z właściwości ActualWidth, ActualHeight (Width i Height mogą być = null !) Rozmiar okna głównego WPF dopuszcza sztywne ustawienie rozmiaru okna. Kiedy i dlaczego? Aby rozmiar okna dostosowywał się do zawartości należy: Usunąć właściwości Width i Height okna, Ustawić Window.SizeToContent na (WidthAndHeight, Width lub Height) WrapPanel rozmieszcza komponenty kolejno w jednym wierszu, a gdy zabraknie miejsca przechodzi do kolejnego wiersza (oczywiście dla WrapPanel.Orientation = Horizontal). <WrapPanel > <Label MinHeight="60">Stack Panel</Label> <Button VerticalAlignment="Top" >Button 1</Button> <Button >Button 2</Button> <TextBox >TextBox 1</TextBox> <Button VerticalAlignment="Bottom" >Button 3</Button> <Button VerticalAlignment="Center">Button 4</Button> </WrapPanel> DockPanel rozmieszcza komponenty wzdłuż krawędzi kolejność dodawania komponentów jest istotna! (Horizontal|Vertical)Alignment są brane pod uwagę <DockPanel LastChildFill="True"> <Label DockPanel.Dock="Left">Stack Panel</Label> <Button DockPanel.Dock="Top">Button 1</Button> <Button DockPanel.Dock="Right">Button 2</Button> <TextBox DockPanel.Dock="Top" >TextBox 1</TextBox> <Button DockPanel.Dock="Bottom">Button 3</Button> <Button >Button 4</Button> </DockPanel> Grid najbardziej zaawansowany pojemnik tworzenie rozkładu za pomocą tego pojemnika przebiega dwuetapowo Definicja wierszy (Grid.RowDefinitions) i kolumn (Grid.ColumnDefinitions) Rozmieszczenie komponentów – właściwości Grid.Row i Grid.Column domyślnie 1 komórka – 1 komponent włożenie dwóch elementów do komórki powoduje ich nałożenie Komponent UniformGrid – uproszczony Grid Grid (2) <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <Label Grid.Row="0" Grid.Column="0" >Grid</Label> <Button Grid.Row="0" Grid.Column="1">Button 1</Button> <Button Grid.Row="0" Grid.Column="2">Button 2</Button> <TextBox Grid.Row="1" Grid.Column="1">TextBox 1</TextBox> <Button Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"> Button 3 </Button> <Button Grid.Row="1" Grid.Column="0">Button 4</Button> </Grid> Grid – rozmiary wierszy i kolumn rozmiary wierszy i kolumn podlegają jednej z następujących reguł, od której zależy sposób obsługi zmiany rozmiaru komponentu w przypadku zwiększenia rozmiaru pojemnika: stały rozmiar – nieelastyczne <RowDefinition Height="80"/> automatyczny – wiersz/kolumna dostają dokładnie tyle miejsca ile potrzebują <RowDefinition Height="Auto"/> proporcjonalny – cały dostępny rozmiar jest proporcjonalnie dzielony przez wiersze/kolumny – wartość domyślna <ColumnDefinition Width="*"/> <ColumnDefinition Width="2*"/> <ColumnDefinition Width="Auto"/> Grid – rozmiary wierszy i kolumn <Grid.RowDefinitions> <RowDefinition Height="80"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="*"/> <ColumnDefinition Width="2*"/> <ColumnDefinition Width="Auto"/> </Grid.ColumnDefinitions> Grid – „Rozpiętość” (Span) kolumn i wierszy <Grid> ... <Label Grid.Row="0" Grid.Column="0" >Grid</Label> <Button Grid.Row="0" Grid.Column="1" Grid.ColumnSpan="2">Button 1 </Button> <TextBox Grid.Row="1" Grid.Column="1"> TextBox 1 </TextBox> <Button Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center"> Button 3 </Button> <Button Grid.Row="1" Grid.Column="0">Button 4</Button> </Grid> Grid – „paski podziału” (splitter bars) umożliwia zmianę rozmiaru wierszy/kolumn użytkownikowi aplikacji pasek podziału musi być umieszczony w komórce rozmiar wiersza/kolumny powinien zostać ustawiony na stałe lub automatycznie rozszerzana jest zawsze cała kolumna/wiersz, nawet jeśli GridSplitter zajmuje tylko jedną komórkę domyślny rozmiar GridSplitter-a jest 0. Należy ustawić następujące właściwości: Podział pionowy: Width, VerticalAlignment=Stretch i HorizontalAlignment=Center Podział poziomy: Height, HorizontalAlignment=Stretch i VerticalAlignment=Center GridSplitter <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition Width="Auto"/> <ColumnDefinition /> </Grid.ColumnDefinitions> <Label Grid.Row="0" Grid.Column="0" >Grid</Label> <Button Grid.Row="0" Grid.Column="2">Button 1</Button> <Button Grid.Row="1" Grid.Column="2" HorizontalAlignment="Center" VerticalAlignment="Center">Button 3</Button> <Button Grid.Row="1" Grid.Column="0">Button 4</Button> <GridSplitter Grid.Column="1" Grid.Row="0" Grid.RowSpan="1" Width="10„ VerticalAlignment="Stretch" HorizontalAlignment="Center"/> </Grid> Canvas umożliwia ręczne umiejscowienie elementów pozycję podaje się w parametrach Canvas.Top (Canvas.Bottom) oraz Canvas.Left (Canvas.Right) Jeżeli elementy nachodzą na siebie, to parametrem Canvas.ZIndex można definiować ich kolejność – element z wyższą wartości zawsze będzie znajdował się nad elementem z niższą wartością Zindex. <Canvas> <Button Canvas.Left="100" Canvas.Top="20">Button 1</Button> <Button Canvas.Top="50" Canvas.Right="20" Width="100" VerticalAlignment="Stretch" Canvas.ZIndex="1">Button 2</Button> <Button Canvas.Top="60" Canvas.Right="40" Height="80"> Button 3 </Button> </Canvas> Ramka - Border <Border Margin="5" Padding="5" Background="LightYellow" BorderBrush="SteelBlue" BorderThickness="3,5,3,5" CornerRadius="3" > <Grid> ... </Grid> </Border> DependencyProperties Podstawowe informacje Dependency Property stanowi rozszerzenie pojęcia właściwości z .NETa na potrzeby interfejsów użytkownika w WPF Jest to podstawowy mechanizm wykorzystywany w animacji elementów interfejsu, wiązaniu danych (data binding) oraz obsłudze styli DP umożliwiają: informowanie o zmianie wartości (change notification) dziedziczenie wartości właściwości zmniejszają koszty przechowywania wartości Tworzenie Depencency Property można je dodać tylko do obiektów dziedziczących po DependencyObject public static readonly DependencyProperty AquariumGraphicProperty; obiekt DependencyProperty musi być zawsze statyczny i tylko do odczytu obiekt ten należy zarejestrować przed jakimkolwiek użyciem klasy dla której go definiujemy static MainWindow() { AquariumGraphicProperty = DependencyProperty.Register( "AquariumGraphic", typeof(Uri), typeof(MainWindow), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.AffectsRender, new PropertyChangedCallback(OnUriChanged)) ); } Tworzenie Dependency Property (2) Rejestrując DP należy podać: jej nazwę typ danych używany przez tę właściwość typ danych, który będzie taką właściwość posiadał opcjonalnie: obiekt FrameworkPropertyMetadata z dodatkowymi ustawieniami opcjonalnie: funkcję zwrotną do walidacji wartości Ustawienia FrameworkPropertyMetadata: AffectsArrange,AffectsMeasure, AffectsParrentArrange, AffectsParentMeasure, AffectsRender, BindsTwoWayByDefault, Inherits, IsAnimationProhibited, IsNotDataBindable, DefaultValue, CoerceValueCallback, PropertyChangedCallback Tworzenie Dependency Property (3) dodanie wrapper-a właściwości public Uri AquariumGraphic { get { return (Uri)GetValue(AquariumGraphicProperty); } set { SetValue(AquariumGraphicProperty, value); } } walidacja danych nie następuje w setterach/getterach! właściwości mogą być współdzielone TextBlock.FontFamilyProperty = TextElement.FontFamilyProperty.AddOwner(typeof(TextBlock)); Właściwości mogą być powiązane – użycie RegisterAttached() Usuwanie wartości właściwości: Window1.ClearValue(MainWindow.AquariumGraphicProperty); Wykorzystanie DP przez WPF informowanie o zmianach (change notification) DP nie uruchamia automatycznie zdarzeń w momencie zmiany wartości Uruchamia metodę OnPropertyChangedCallback(), która to przekazuje informację o zmianie wartości do dwóch serwisów: wiązania danych i triggerów – chcąc reagować na zmianę wartości można albo utworzyć wiązanie albo napisać trigger Niektóre komponenty udostępniają zdarzenia po zmianie wartości np. ScrollBar – ValueChanged Wykorzystanie DP przez WPF wyznaczanie wartości (dynamic value resolution) Określenie bazowej wartości właściwości odbywa się na podstawie: Wartości domyślnej, Wartości dziedziczonej, Wartości dla styli lokalnej wartości Następnie wartość końcowa określana jest w następujący sposób: Jeżeli wartość opisana jest jakimś wyrażeniem – wyznaczenie jej wartości (data binding i zasoby) Jeżeli wartość jest celem animacji – zastosowanie animacji Wywołanie metody CoerceValueCallback w celu „naprawienia” wartości Walidacja wartości Ustawienie wartości DP przebiega następująco: Dostarczana wartość trafia do metody CoerceValueCallback, gdzie może zostać zmodyfikowana. Jeśli zmiana jest nieakceptowalna zwracana jest wartość DependencyProperty.UnsetValue Następnie uruchamiana jest metoda ValidateValueCallback, która zwraca wartość true jeśli wartość jest akceptowana. Metoda ta nie ma dostępu do pozostałych właściwości obiektu. Jeśli poprzednie kroki zakończą się sukcesem, zostaje wywołana metoda PropertyChangedCallback ValidateValueCallback musi być statyczna i jest dostarczana jako opcjonalna na etapie rejestracji DP CoerceValueCallback ma następujący nagłówek private static object CoerceMaximum( DependencyObject d, object value); Przykład działania walidacji ScrollBar bar = new ScrollBar(); bar.Value = 100; //bar.Value = 1 bar.Minimum = 1; //bar.Value = 1 bar.Maximum = 200; //bar.Value = 100