在很多工廠的信息化MES系統中,車間的采集數據的機器是觸摸屏電腦(工廠環境所限,用外接鼠標鍵盤反而不方便)。
由於沒有外接鍵盤,所以用戶無法像坐在辦公室一樣,用鼠標鍵盤進行錄入信息。
這時我們可以用wpf的Popup控件,當點擊一個"文本框"時,彈出一個自定義的鍵盤(UserControl),實現如下圖效果:
自定義用戶控件(UserControl),"鍵盤":
Xaml:

<UserControl x:Class="PopupDemo.NumericKeyboard" 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" mc:Ignorable="d"> <UserControl.Resources> <Style x:Key="btnNum" TargetType="{x:Type Button}"> <!--<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/>--> <!--<Setter Property="Background"> <Setter.Value> <LinearGradientBrush EndPoint="0,1" StartPoint="0,0"> <GradientStop Color="#F3F3F3" Offset="0"/> <GradientStop Color="#EBEBEB" Offset="0.5"/> <GradientStop Color="#DDDDDD" Offset="0.5"/> <GradientStop Color="#CDCDCD" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter>--> <!--<Setter Property="BorderBrush" Value="#FF707070"/>--> <!--<Setter Property="BorderThickness" Value="1"/>--> <!--<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>--> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <!--<Setter Property="Height" Value="64" /> <Setter Property="Width" Value="104" />--> <!--<Setter Property="Padding" Value="10"/>--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Border x:Name="border" BorderBrush="#FF474747" BorderThickness="1" CornerRadius="6"> <!--<Border.Effect> <DropShadowEffect BlurRadius="1" ShadowDepth="1" Opacity="0.27"/> </Border.Effect>--> <Border.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFCCCCCC"/> <GradientStop Color="WhiteSmoke" Offset="1"/> </LinearGradientBrush> </Border.Background> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" TextElement.Foreground="#FF565656" TextElement.FontSize="24" /> <!--<TextBlock TextWrapping="Wrap" Text="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="21.333" Foreground="#FF565656"/>--> </Border> <ControlTemplate.Triggers> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" TargetName="border"> <Setter.Value> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFFF7500"/> <GradientStop Color="#FFFFC547" Offset="1"/> </LinearGradientBrush> </Setter.Value> </Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="btnFunc" TargetType="{x:Type Button}"> <!--<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/> <Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/> <Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>--> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <!--<Setter Property="Padding" Value="1"/>--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Border x:Name="border" BorderBrush="#FF565656" BorderThickness="1" CornerRadius="6"> <Border.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFFFC547" Offset="1"/> <GradientStop Color="#FFF77202"/> </LinearGradientBrush> </Border.Background> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" TextElement.Foreground="White" TextElement.FontSize="24"/> <!--<TextBlock TextWrapping="Wrap" Text="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontSize="21.333"/>--> </Border> <ControlTemplate.Triggers> <Trigger Property="IsPressed" Value="True"> <Setter Property="Background" TargetName="border"> <Setter.Value> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="Gray" Offset="1"/> <GradientStop Color="#FF2D2D2D" Offset="0"/> </LinearGradientBrush> </Setter.Value> </Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style TargetType="{x:Type ToggleButton}"> <!--<Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/> <Setter Property="Background" Value="{StaticResource ButtonNormalBackground}"/> <Setter Property="BorderBrush" Value="{StaticResource ButtonNormalBorder}"/> <Setter Property="BorderThickness" Value="1"/> <Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>--> <Setter Property="HorizontalContentAlignment" Value="Center"/> <Setter Property="VerticalContentAlignment" Value="Center"/> <!--<Setter Property="Padding" Value="1"/>--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ToggleButton}"> <Border x:Name="border" BorderBrush="#FF565656" BorderThickness="1" CornerRadius="6"> <Border.Background> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFFFC547" Offset="1"/> <GradientStop Color="#FFF77202"/> </LinearGradientBrush> </Border.Background> <ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" TextElement.Foreground="White" TextElement.FontSize="24"/> <!--<TextBlock TextWrapping="Wrap" Text="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontSize="21.333"/>--> </Border> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="False"> <Setter Property="Background" TargetName="border"> <Setter.Value> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="Gray" Offset="1"/> <GradientStop Color="#FF2D2D2D" Offset="0"/> </LinearGradientBrush> </Setter.Value> </Setter> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </UserControl.Resources> <Grid> <Grid.RowDefinitions> <RowDefinition /> <RowDefinition /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Button Margin="2" Grid.Column="0" Grid.Row="0" Content="1" x:Name="button1" Click="button1_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="1" Grid.Row="0" Content="2" x:Name="button2" Click="button2_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="2" Grid.Row="0" Content="3" x:Name="button3" Click="button3_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="0" Grid.Row="1" Content="4" x:Name="button4" Click="button4_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="1" Grid.Row="1" Content="5" x:Name="button5" Click="button5_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="2" Grid.Row="1" Content="6" x:Name="button6" Click="button6_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="0" Grid.Row="2" Content="7" x:Name="button7" Click="button7_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="1" Grid.Row="2" Content="8" x:Name="button8" Click="button8_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="2" Grid.Row="2" Content="9" x:Name="button9" Click="button9_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="1" Grid.Row="3" Content="0" x:Name="button0" Click="button0_Click" Style="{StaticResource btnNum}"/> <Button Margin="2" Grid.Column="0" Grid.Row="3" Content="清零" x:Name="btnMin" Click="btnMin_Click" Style="{StaticResource btnFunc}" /> <Button Margin="2" Grid.Column="2" Grid.Row="3" Content="確認" x:Name="btnClose" Click="btnClose_Click" Style="{StaticResource btnFunc}" /> </Grid> </UserControl>
C#:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace PopupDemo { /// <summary> /// NumericKeyboard.xaml 的交互邏輯 /// </summary> public partial class NumericKeyboard : UserControl { public NumericKeyboard() { InitializeComponent(); } public bool HasMaxValue { get { return (bool)GetValue(HasMaxValueProperty); } set { SetValue(HasMaxValueProperty, value); } } // Using a DependencyProperty as the backing store for HasMaxValue. This enables animation, styling, binding, etc... public static readonly DependencyProperty HasMaxValueProperty = DependencyProperty.Register("HasMaxValue", typeof(bool), typeof(NumericKeyboard), new UIPropertyMetadata(true)); public int MaxValue { get { return (int)GetValue(MaxValueProperty); } set { SetValue(MaxValueProperty, value); } } // Using a DependencyProperty as the backing store for MaxValue. This enables animation, styling, binding, etc... public static readonly DependencyProperty MaxValueProperty = DependencyProperty.Register("MaxValue", typeof(int), typeof(NumericKeyboard), new UIPropertyMetadata(0)); public int Value { get { return (int)GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } // Using a DependencyProperty as the backing store for Value. This enables animation, styling, binding, etc... public static readonly DependencyProperty ValueProperty = DependencyProperty.Register("Value", typeof(int), typeof(NumericKeyboard), new UIPropertyMetadata(0)); public bool IsChecked { get { return (bool)GetValue(IsCheckedProperty); } set { SetValue(IsCheckedProperty, value); } } // Using a DependencyProperty as the backing store for IsChecked. This enables animation, styling, binding, etc... public static readonly DependencyProperty IsCheckedProperty = DependencyProperty.Register("IsChecked", typeof(bool), typeof(NumericKeyboard), new UIPropertyMetadata(false)); private void btnMin_Click(object sender, RoutedEventArgs e) { //if (Item != null && Item.Quantity1 > 0) //{ // Item.Quantity2 = Item.Quantity1; //} Value = 0; } private void AddNumber(int num) { //if (Item != null) //{ // int cnt = Item.Quantity2 * 10 + num; // if (Item.Quantity1 > 0 && cnt > Item.Quantity1) // { // Item.Quantity2 = Item.Quantity1; // } // else // { // Item.Quantity2 = cnt; // } //} if (HasMaxValue) { Value = Math.Min(MaxValue, Value * 10 + num); } else { Value = Value * 10 + num; } } private void button2_Click(object sender, RoutedEventArgs e) { AddNumber(2); } private void button3_Click(object sender, RoutedEventArgs e) { AddNumber(3); } private void button4_Click(object sender, RoutedEventArgs e) { AddNumber(4); } private void button5_Click(object sender, RoutedEventArgs e) { AddNumber(5); } private void button6_Click(object sender, RoutedEventArgs e) { AddNumber(6); } private void button7_Click(object sender, RoutedEventArgs e) { AddNumber(7); } private void button8_Click(object sender, RoutedEventArgs e) { AddNumber(8); } private void button9_Click(object sender, RoutedEventArgs e) { AddNumber(9); } private void button0_Click(object sender, RoutedEventArgs e) { AddNumber(0); } private void button1_Click(object sender, RoutedEventArgs e) { AddNumber(1); } private void btnClose_Click(object sender, RoutedEventArgs e) { IsChecked = false; } } }
主窗體:
Xaml:

<Window x:Class="PopupDemo.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:PopupDemo" Title="MainWindow" Width="700" Height="600" WindowStartupLocation="CenterScreen" DataContext="{Binding RelativeSource={RelativeSource Self}}"> <Window.Resources> <Style TargetType="{x:Type Button}" x:Key="rightButton"> <Setter Property="Margin" Value="5" /> <Setter Property="Padding" Value="10" /> <!--<Setter Property="FontSize" Value="20" /> <Setter Property="Foreground" Value="White" />--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type Button}"> <Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0" CornerRadius="3,13,3,13" Padding="{TemplateBinding Padding}"> <Border.Background> <SolidColorBrush Color="#FF6C6C6C" /> </Border.Background> <ContentPresenter x:Name="content" HorizontalAlignment="Center" VerticalAlignment="Center" TextElement.FontSize="20" TextElement.Foreground="White" /> </Border> <ControlTemplate.Triggers> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="border" Property="Background" Value="#FF518CB1" /> </Trigger> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="content" Property="TextElement.Foreground" Value="Gray" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="RadioButtonStyle" TargetType="{x:Type RadioButton}"> <!--<Setter Property="Margin" Value="5" />--> <!--<Setter Property="FontSize" Value="20" />--> <!--<Setter Property="Foreground" Value="White" />--> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type RadioButton}"> <Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="0" CornerRadius="4" Background="Transparent" Padding="10,2" x:Name="PART_Background"> <ContentPresenter x:Name="PART_Content" VerticalAlignment="Center" TextElement.Foreground="Black" /> </Border> <ControlTemplate.Triggers> <Trigger Property="IsChecked" Value="True"> <Setter TargetName="PART_Background" Property="Background"> <Setter.Value> <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> <GradientStop Color="#FFFFC547" Offset="1"/> <GradientStop Color="#FFF77202"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter TargetName="PART_Content" Property="TextElement.Foreground" Value="White" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> </Window.Resources> <Grid> <DockPanel DockPanel.Dock="Right" Margin="0,40,0,0" Width="120" LastChildFill="False"> <Button x:Name="btnShow" Content="Show" DockPanel.Dock="Top" Margin="0,40,0,0" Click="btnShow_Click" Style="{StaticResource ResourceKey=rightButton}" /> <RadioButton x:Name="btnTerminalNo" DockPanel.Dock="Top" Margin="0,40,0,0" Style="{StaticResource RadioButtonStyle}"> <StackPanel Orientation="Vertical"> <TextBlock Text="終端編號" FontSize="20" TextAlignment="Center"/> <Popup StaysOpen="False" DockPanel.Dock="Bottom" IsOpen="{Binding Path=IsChecked, RelativeSource={RelativeSource FindAncestor, AncestorType=RadioButton, AncestorLevel=1}}" PopupAnimation="Scroll" AllowsTransparency="True"> <Border Background="White" BorderBrush="Black" BorderThickness="1" Padding="4" Width="360" Height="280"> <local:NumericKeyboard HasMaxValue="True" MaxValue="9999" IsChecked="{Binding Path=IsChecked, RelativeSource={RelativeSource FindAncestor, AncestorType=RadioButton, AncestorLevel=1}, Mode=TwoWay}" Value="{Binding Path=TerminalNo, Mode=TwoWay}" /> </Border> </Popup> <Border Background="Gray" CornerRadius="5" BorderBrush="Black" BorderThickness="2" Padding="5" Margin="0,0,0,5" HorizontalAlignment="Stretch"> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center" Foreground="White" FontWeight="Bold" FontSize="32" Text="{Binding Path=TerminalNo}" /> </Border> </StackPanel> </RadioButton> </DockPanel> </Grid> </Window>
C#:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using System.ComponentModel; namespace PopupDemo { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window,INotifyPropertyChanged { private int terminalNo; public int TerminalNo { get { return terminalNo; } set { if(value!=terminalNo) { terminalNo = value; OnPropertyChanged("TerminalNo"); } } } public MainWindow() { InitializeComponent(); } public event PropertyChangedEventHandler PropertyChanged; public void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } private void btnShow_Click(object sender, RoutedEventArgs e) { MessageBox.Show(string.Format("終端編號的值為{0}", TerminalNo)); } } }
驗證輸入后,綁定TextBlock的Text屬性上值是否真的變化了:
總結:
1.Popup控件永遠不會自動顯示,為了顯示Popup控件必須設置IsOpen屬性。
2.默認情況下,Popup.StaysOen屬性被設置為True,並且Popup控件會一直顯示,直到顯式地將IsOpen屬性設置為False;
如果將Popup.StaysOpen屬性設置為False,當用戶在其他地方單擊鼠標時,Popup效果關閉。
如果Popup控件的IsOpen屬性設置為True時,通過Popup控件的PopupAnimation屬性(同時必須要設置屬性 AllowsTransparency="True")
可以設置Popup控件的顯示方式: