2010-02-04 58 views
21

有沒有辦法使用WPF Trigger s將Focus從一個控件設置爲另一個控件?WPF - 單擊按鈕時設置焦點 - 沒有代碼

像下面的例子:

<Page 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> 
    <Grid> 
    <Grid.RowDefinitions> 
     <RowDefinition/> 
     <RowDefinition/> 
     <RowDefinition/> 
    </Grid.RowDefinitions> 

    <TextBox Name="txtName"></TextBox>  
    <TextBox Grid.Row="1" Name="txtAddress"></TextBox> 
    <Button Grid.Row="2" Content="Finish"> 
     <Button.Triggers> 
      <EventTrigger RoutedEvent="Button.Click"> 

       <!-- Insert cool code here--> 

      </EventTrigger> 
     </Button.Triggers> 
    </Button> 
    </Grid> 
</Page> 

有沒有辦法爲這個EventTrigger把專注於文本框「改爲txtName」?

我試圖找到方法來使用嚴格的MVVM來做這樣的事情。如果這是不應該通過XAML(在MVVM中)完成的事情,那很好。但是我希望看到一些關於它如何適應MVVM模式的文檔,在XAML之外進行。

回答

28

你有沒有使用附加的行爲考慮。他們很容易實現並使用AttachedProperty's。雖然它仍然需要代碼,但是這些代碼在一個類中被抽象出來並被重用。他們可以消除需要的代碼,並且經常與MVVM模式一起使用。

試試這個,看看它是否適合你。

public class EventFocusAttachment 
{ 
    public static Control GetElementToFocus(Button button) 
    { 
     return (Control)button.GetValue(ElementToFocusProperty); 
    } 

    public static void SetElementToFocus(Button button, Control value) 
    { 
     button.SetValue(ElementToFocusProperty, value); 
    } 

    public static readonly DependencyProperty ElementToFocusProperty = 
     DependencyProperty.RegisterAttached("ElementToFocus", typeof(Control), 
     typeof(EventFocusAttachment), new UIPropertyMetadata(null, ElementToFocusPropertyChanged)); 

    public static void ElementToFocusPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e) 
    { 
     var button = sender as Button; 
     if (button != null) 
     { 
      button.Click += (s, args) => 
       { 
        Control control = GetElementToFocus(button); 
        if (control != null) 
        { 
         control.Focus(); 
        } 
       }; 
     } 
    } 
} 

,然後在XAML做這樣的事情......

<Button 
    Content="Click Me!" 
    local:EventFocusAttachment.ElementToFocus="{Binding ElementName=textBox}" 
    /> 
<TextBox x:Name="textBox" /> 
+0

我喜歡這個。我會嘗試一下。 – Vaccano 2010-02-06 20:02:35

+0

這很好。雖然它使用代碼,但它並不是窗口後面的代碼,所以我可以確定。謝謝! – Vaccano 2010-02-06 23:50:36

+0

這是我能夠使用DataTemplate並從ViewModel集中文本框的唯一工作解決方案。謝謝。 – alexandrudicu 2013-05-14 13:05:23

12

不知道,但我想你可以做這樣的事情:

FocusManager.FocusedElement="{Binding ElementName=txtName}"> 

原諒我,如果我錯了,我不是由Visual Studio,所以我不能對它進行測試。

+0

我喜歡這個,但我需要更多的代碼來看看它是如何工作的。我找不到使用EventTrigger設置的方法。 (故事板無法做到這一點,我可以告訴。) – Vaccano 2010-02-05 16:33:57

+0

@Vaccano - 我對話很遲,但如果你在原來發布的點擊事件之後想要改變焦點,你根本不需要EventTrigger,你可以設置caesay指出的聚焦元素屬性......一旦按鈕被點擊,它會將焦點改變爲綁定元素。 – Aaj 2012-04-30 14:14:06

+0

它正如OP所要求的那樣在這裏工作。幹得好,凱撒! – 2016-04-18 22:57:37

1

這是你想要的嗎?

<TextBox Name="txtName"></TextBox> 
    <TextBox Grid.Row="1" Name="txtAddress"></TextBox> 
    <Button Grid.Row="2" Content="Finish"> 
     <Button.Style> 
      <Style TargetType="{x:Type Button}"> 
       <EventSetter Event="Click" Handler="MoveFocusOnClick" /> 
      </Style> 
     </Button.Style> 
     <!--<Button.Triggers> 
      <EventTrigger RoutedEvent="Button.Click"> 
      </EventTrigger> 
     </Button.Triggers>--> 
    </Button> 

C#:

public void MoveFocusOnClick(object sender, RoutedEventArgs e) 
    { 
     Keyboard.Focus(txtName); // Or your own logic 
    } 
+0

是的,如果我願意使用後面的代碼,那麼有幾種方法可以實現這一點。我想知道是否有辦法在沒有代碼的情況下執行此操作。 – Vaccano 2010-02-05 16:34:40

8

你也可以使用一個WPF行爲......

public class FocusElementAfterClickBehavior : Behavior<ButtonBase> 
{ 
    private ButtonBase _AssociatedButton; 

    protected override void OnAttached() 
    { 
     _AssociatedButton = AssociatedObject; 

     _AssociatedButton.Click += AssociatedButtonClick; 
    } 

    protected override void OnDetaching() 
    { 
     _AssociatedButton.Click -= AssociatedButtonClick; 
    } 

    void AssociatedButtonClick(object sender, RoutedEventArgs e) 
    { 
     Keyboard.Focus(FocusElement); 
    } 

    public Control FocusElement 
    { 
     get { return (Control)GetValue(FocusElementProperty); } 
     set { SetValue(FocusElementProperty, value); } 
    } 

    // Using a DependencyProperty as the backing store for FocusElement. This enables animation, styling, binding, etc... 
    public static readonly DependencyProperty FocusElementProperty = 
     DependencyProperty.Register("FocusElement", typeof(Control), typeof(FocusElementAfterClickBehavior), new UIPropertyMetadata()); 
} 

下面是使用行爲的XAML 。

包括命名空間:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity" 
xmlns:local="clr-namespace:WpfApplication1" 

連接WPF行爲對按鈕和綁定元素要設置焦點:

<Button Content="Focus" Width="75"> 
    <i:Interaction.Behaviors> 
     <local:FocusElementAfterClickBehavior FocusElement="{Binding ElementName=CheckBoxComboBox, Mode=OneWay}"/> 
    </i:Interaction.Behaviors> 
</Button> 
<ComboBox x:Name="CheckBoxComboBox" HorizontalAlignment="Center" VerticalAlignment="Center" Width="120" Grid.Row="1"/> 

所以這樣你就沒有後面的代碼,它是可重複使用的任何從ButtonBase繼承的控件。

希望這可以幫助別人。

+0

這比附加屬性更好,因爲它具有更好的混合支持。如果你不使用混合,那麼這隻需要更多的xaml代碼。 – 2011-03-02 03:30:16

+0

不要忘記添加一個對System.Windows.Interactivity的引用。 – 2011-04-21 10:15:39

0

這和Ian Oakes的解決方案是一樣的,但我做了一些小的改動。

  1. 按鈕類型可以更一般化,即ButtonBase,以處理更多的情況,如ToggleButton
  2. 目標類型也可以更一般化,即UIElement。從技術上講,我想這可能是IInputElement
  3. 我將事件處理程序設置爲靜態,以便它不會每次都生成運行時關閉。爲了確保它保持靜態,我將它從lambda中移出。

非常感謝伊恩。


public sealed class EventFocusAttachment 
{ 
    [DebuggerStepThrough] 
    public static UIElement GetTarget(ButtonBase b) { return (UIElement)b.GetValue(TargetProperty); } 

    [DebuggerStepThrough] 
    public static void SetTarget(ButtonBase b, UIElement target) { b.SetValue(TargetProperty, target); } 

    public static readonly DependencyProperty TargetProperty = 
     DependencyProperty.RegisterAttached("Target", 
              typeof(UIElement), 
              typeof(EventFocusAttachment), 
              new UIPropertyMetadata(null, TargetPropertyChanged)); 

    public static void TargetPropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs _) 
    { 
     ButtonBase bb; 
     if ((bb = o as ButtonBase) != null) 
      bb.Click += handler; 
    } 

    static void handler(Object o, RoutedEventArgs _) 
    { 
     UIElement target; 
     if ((target = GetTarget((ButtonBase)o)) != null) 
      target.Focus(); 
    } 
}; 

用法,基本上與上述相同:

<ToggleButton z:EventFocusAttachment.Target="{Binding RelativeSource={RelativeSource Self}}" /> 

注意,事件可以靶向/集中始發按鈕本身。

3

您需要一個TriggerAction來調用所需控件的Focus()方法。

public class SetFocusTrigger : TargetedTriggerAction<Control> 
{ 
protected override void Invoke(object parameter) 
{ 
    if (Target == null) return; 

    Target.Focus(); 
} 
} 

若要設置爲控制重點,你把一個觸發器收集您的LayoutRoot後(或任何控制真的),選擇事件作爲觸發,並選擇SetFocusTrigger作爲類運行。在SetFocusTrigger聲明中,通過使用TargetName屬性來放置要接收焦點的控件的名稱。

<Button x:Name="LayoutRoot" > 
<i:Interaction.Triggers> 
    <i:EventTrigger EventName="Clicked"> 
     <local:SetFocusTrigger TargetName="StartHere"/> 
    </i:EventTrigger> 
</i:Interaction.Triggers> 

<TextBox x:Name="StartHere"/> 
</Button> 
+0

這是一個很酷的想法,也是一個非常緊湊的解決方案。 – 2016-10-28 09:00:29