win10 uwp 按下等待按鈕
我們經常需要一個按鈕,在按下時,後臺執行Task,這時不能再次按下按鈕。
我們使用自定義控制元件,首先新建一個類,我把它命名是ProgressButton
一個進度條按鈕,也就是我們按下時發生進度條,完成時他又是按鈕。
我們需要一個值讓我們知道是不是已經完成了後臺,按鈕可以按下,在按下時,自動讓按鈕IsEnable為false。
我們需要模板有TextBlock,顯示文字,ProgressRing顯示進度條。
於是我們使用TemplatePart
[TemplatePart(Name = "TextBlock", Type = typeof(TextBlock))]
[TemplatePart(Name = "Progress" , Type = typeof(Windows.UI.Xaml.Controls.ProgressRing))]
public class ProgressButton : Windows.UI.Xaml.Controls.Button
依賴屬性其實很簡單,我們需要在VS上大propdp 按Tab 就可以看到vs幫我們寫的依賴屬性。
我們需要修改屬性名稱,屬性型別,預設值。
我這裡的Text ,需要他修改時使用函式,這個叫CallBack。
依賴函式使用DependencyProperty.Register
他引數: name 是 屬性名, propertyType 是屬性型別, ownerType 是屬於的類的型別, typeMetadata 是預設值和修改時使用函式
我們來說下 typeMetadata
typeMetadata 可以傳入一個預設值,這個值就是我們不在依賴屬性賦值,就給他一個預設的值。然後我們還可以給他一個在屬性修改時使用的函式。
注意我們給他的函式不是必需,一般都不需要。
如果需要給他一個函式,這個函式需要有引數DependencyObject sender, DependencyPropertyChangedEventArgs e
其中 sender 是傳送的例項,我們知道屬性屬於哪個類,我在這裡,是屬於ProgressButton ,我就可以使用 ProgressButton button = sender as ProgressButton;
e 是有 NewValue 和 OldValue , NewValue 是我們要修改的值, OldValue 是原來的值。
大概需要的依賴屬性在我們這個控制元件有 Text Complete 就沒了。
Text是我們按鈕的文字,Complete 是我們的後臺是不是在執行,如果是的話,按鈕就無法點選,顯示進度條。
程式碼:
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
namespace lindexi.uwp.control.Button.Control
{
[TemplatePart(Name = "TextBlock", Type = typeof(TextBlock))]
[TemplatePart(Name = "Progress", Type = typeof(Windows.UI.Xaml.Controls.ProgressRing))]
public class ProgressButton : Windows.UI.Xaml.Controls.Button
{
public ProgressButton()
{
DefaultStyleKey = typeof(ProgressButton);
Click += ProgressButton_Click;
}
private void ProgressButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
Complete = false;
}
private Windows.UI.Xaml.Controls.TextBlock _textBlock;
private Windows.UI.Xaml.Controls.ProgressRing _proress;
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
// Using a DependencyProperty as the backing store for Text. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(ProgressButton), new PropertyMetadata("",
(d, e) =>
{
ProgressButton temp = d as ProgressButton;
if (temp == null)
{
return;
}
if(temp._textBlock!=null)
{
temp._textBlock.Text = (string) e.NewValue;
}
}));
public bool Complete
{
get { return (bool)GetValue(CompleteProperty); }
set { SetValue(CompleteProperty, value); }
}
// Using a DependencyProperty as the backing store for Complete. This enables animation, styling, binding, etc...
public static readonly DependencyProperty CompleteProperty =
DependencyProperty.Register("Complete", typeof(bool), typeof(ProgressButton), new PropertyMetadata(true,
OnComplete));
private static void OnComplete(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
ProgressButton button = d as ProgressButton;
if (button == null)
{
return;
}
bool temp = (bool)e.NewValue;
//button._textBlock.Visibility = temp ? Visibility.Visible : Visibility.Collapsed;
button._proress.Visibility = temp ? Visibility.Collapsed : Visibility.Visible;
button.IsEnabled = temp;
}
protected override void OnApplyTemplate()
{
base.OnApplyTemplate();
_textBlock = GetTemplateChild("TextBlock") as Windows.UI.Xaml.Controls.TextBlock;
_proress = GetTemplateChild("Progress") as Windows.UI.Xaml.Controls.ProgressRing;
if (_textBlock != null)
{
_textBlock.Visibility = Visibility.Visible;
_textBlock.Text = Text;
}
if (_proress != null)
{
_proress.Visibility=Visibility.Collapsed;
}
}
}
}
我們在控制元件 OnApplyTemplate 拿到 _textBlock _proress 我們需要寫一個Style
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:control="using:lindexi.uwp.control.Button.Control">
<Style TargetType="control:ProgressButton">
<Setter Property="Background" Value="{ThemeResource ButtonBackground}"/>
<Setter Property="Foreground" Value="{ThemeResource ButtonForeground}"/>
<Setter Property="BorderBrush" Value="{ThemeResource ButtonBorderBrush}"/>
<Setter Property="BorderThickness" Value="{ThemeResource ButtonBorderThemeThickness}"/>
<Setter Property="Padding" Value="8,4,8,4"/>
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="VerticalAlignment" Value="Center"/>
<Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}"/>
<Setter Property="UseSystemFocusVisuals" Value="True"/>
<Setter Property="FocusVisualMargin" Value="-3"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="control:ProgressButton">
<Grid x:Name="RootGrid" Background="{TemplateBinding Background}">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal">
<Storyboard>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="PointerOver">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="RootGrid">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPointerOver}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushPointerOver}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundPointerOver}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundPointerOver}"></DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
<PointerUpThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="RootGrid">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundPressed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushPressed}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundPressed}"/>
</ObjectAnimationUsingKeyFrames>
<PointerDownThemeAnimation Storyboard.TargetName="RootGrid"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Background" Storyboard.TargetName="RootGrid">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBackgroundDisabled}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="BorderBrush" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonBorderBrushDisabled}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground" Storyboard.TargetName="ContentPresenter">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundDisabled}"/>
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="TextBlock" Storyboard.TargetProperty="Foreground">
<DiscreteObjectKeyFrame KeyTime="0" Value="{ThemeResource ButtonForegroundDisabled}"></DiscreteObjectKeyFrame>
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<ContentPresenter x:Name="ContentPresenter"
AutomationProperties.AccessibilityView="Raw"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
ContentTemplate="{TemplateBinding ContentTemplate}"
ContentTransitions="{TemplateBinding ContentTransitions}"
Content="{TemplateBinding Content}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
<TextBlock x:Name="TextBlock" Margin="10,10,10,10"
HorizontalAlignment="Center"
VerticalAlignment="Center"></TextBlock>
<ProgressRing x:Name="Progress" IsActive="True"></ProgressRing>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
這個是從Button複製,就改了Button為 control:ProgressButton
我們要使用按鈕,需要在資源寫
<Page.Resources>
<ResourceDictionary Source="Control/ProgressButton.xaml"></ResourceDictionary>
</Page.Resources>
然後就可以使用 ProgressButton ,我寫ProgressButton在control資料夾,我需要在名稱空間xmlns:control="using:lindexi.uwp.control.Button.Control"
<control:ProgressButton Text="確定"
Complete="{x:Bind View.Complete,Mode=TwoWay}"
Click="ButtonBase_OnClick"></control:ProgressButton>
那麼如果我們有好多個頁面都用到 ProgressButton ,我們需要在所有頁面都寫 ResourceDictionary 這樣不好,我們有一個簡單方法,讓頁面不用寫這個。
在解決方案新建一個資料夾Themes,注意命名一定是Themes,注意有個名稱後面有個s,我就在這坑好多天了。
然後新建資源字典 Generic.xaml ,注意名稱也是不能自己修改。
在 Generic.xaml 合併字典
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx:///Control/ProgressButton.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
這樣我們就可以在頁面直接用。
如果使用遇到問題,歡迎討論。