wpf之Popup弹出自定义输入"键盘"
在很多工厂的信息化MES系统中,车间的采集数据的机器是触摸屏电脑(工厂环境所限,用外接鼠标键盘反而不方便)。
由于没有外接键盘,所以用户无法像坐在办公室一样,用鼠标键盘进行录入信息。
这时我们可以用wpf的Popup控件,当点击一个"文本框"时,弹出一个自定义的键盘(UserControl),实现如下图效果:
自定义用户控件(UserControl),"键盘":
Xaml:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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#:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<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#:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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控件的显示方式: