WPF 및 초기 초점


191

WPF 응용 프로그램이 시작될 때 포커스가없는 것 같습니다.

정말 이상합니다. 내가 사용한 다른 모든 프레임 워크는 예상 한대로 수행합니다. 첫 번째 컨트롤에 첫 번째 컨트롤을 탭 순서로 둡니다. 그러나 나는 그것이 내 응용 프로그램뿐만 아니라 WPF임을 확인했습니다. 새 창을 만들고 TextBox를 넣고 응용 프로그램을 실행하면 TextBox는 클릭하거나 Tab 키를 누를 때까지 포커스가 없습니다. . 왝.

내 실제 응용 프로그램은 TextBox보다 더 복잡합니다. UserControls 내에 여러 계층의 UserControl이 있습니다. 이러한 UserControl 중 하나에 Focusable = "True"및 KeyDown / KeyUp 핸들러가 있으며, 창이 열리 자마자 포커스를 갖기를 원합니다. 그래도 여전히 WPF 초보자이지만, 이것을하는 방법을 알아내는 데 많은 운이 없습니다.

앱을 시작하고 Tab 키를 누르면 포커스가 포커스 가능한 컨트롤로 이동하여 원하는 방식으로 작동하기 시작합니다. 그러나 사용자가 창을 사용하기 전에 Tab 키를 누르지 않기를 바랍니다.

FocusManager.FocusedElement를 가지고 놀았지만 컨트롤을 설정할 컨트롤 (최상위 Window 창, 포커스 가능한 컨트롤을 포함하는 부모, 포커스 가능한 컨트롤 자체) 또는 무엇을 설정할지 잘 모르겠습니다.

창을 열 자마자 깊게 중첩 된 컨트롤이 초기 초점을 갖도록하려면 어떻게해야합니까? 또는 탭 순서에서 첫 번째 포커스 가능한 컨트롤에 초점을 맞추는 것이 더 낫습니까?

답변:


165

이것도 작동합니다.

<Window FocusManager.FocusedElement="{Binding ElementName=SomeElement}">

   <DataGrid x:Name="SomeElement">
     ...
   </DataGrid>
</Window>

4
나는 이것에 대해 처음 언급 한 사람인 것에 놀랐습니다. 거의 모든 제어가 가능하기 때문에 이것이 어디로 갔는지 혼란스러워했습니다. 이 특정 질문에 대한 대답으로, 나는 그것이 창에 갈 것이라고 생각하지만, msdn.microsoft.com/en-us/library/… 에 있는 설명을 읽어서 이것을 어떻게 컨트롤에 적용하는지 이해할 수 있습니다.
Joel McBeth

나는이 접근법을 스택 패널에서 성공적으로 사용했습니다. 관심이 있다면 stackoverflow.com/a/2872306/378115
Julio Nobre

이것은 처음에 나오는 요소에 중점을 두어야하기 때문에 허용 된 답변보다 훨씬 나에게 효과적이었습니다.
Puterdo Borato

163

Focusable 속성이 사용되는 위치를 확인하기 위해 Reflector를 파헤 치고이 솔루션으로가는 길을 찾았습니다. 내 창 생성자에 다음 코드를 추가하면됩니다.

Loaded += (sender, e) =>
    MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

이것은 탭 순서에서 첫 번째 컨트롤을 자동으로 선택하므로 모든 창과 Just Work에 놓을 수있는 일반적인 솔루션입니다.


21
그것을 행동으로 바꾸십시오. <Window FocusBehavior.FocusFirst = "true"> ... </ Window>
wekempf

6
@ wekempf, 나는 행동에 대한 생각에 익숙하지 않았지만 그것을 조사했으며 전혀 나쁜 생각이 아닙니다. 나와 같은 다른 사람이 첨부 된 동작에 익숙하지 않은 경우 여기에 설명이 있습니다. codeproject.com/KB/WPF/AttachedBehaviors.aspx
Joe White

1
또한 원하는 요소가 실제 포커스 가능한 요소를 포함하는 UserControl 인 경우에도 작동합니다 (심층 계층 구조에서도). 큰!
Daniel Albuschat

1
좋은 생각이지만 포커스를받는 컨트롤이이면 작동하지 않는 경우가 있습니다 Button. 이 문제를 해결하기 위해 우선 순위에 MoveFocus따라 디스패처로 전화를 ContextIdle넘깁니다 ( Background이상은 작동하지 않습니다). 또한 FocusNavigationDirection.First의도와 더 잘 일치 하고이 경우에도 동일한 작업을 수행하는가 있습니다.
Anton Tykhyy

이것이 기본 동작이어야합니다! Yuck (원래 게시물)이 맞습니다!
NH.

61

첨부 된 동작으로 구현 된 허용 된 답변을 기반으로합니다 .

using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace UI.Behaviors
{
    public static class FocusBehavior
    {
        public static readonly DependencyProperty FocusFirstProperty =
            DependencyProperty.RegisterAttached(
                "FocusFirst",
                typeof(bool),
                typeof(FocusBehavior),
                new PropertyMetadata(false, OnFocusFirstPropertyChanged));

        public static bool GetFocusFirst(Control control)
        {
            return (bool)control.GetValue(FocusFirstProperty);
        }

        public static void SetFocusFirst (Control control, bool value)
        {
            control.SetValue(FocusFirstProperty, value);
        }

        static void OnFocusFirstPropertyChanged(
            DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            Control control = obj as Control;
            if (control == null || !(args.NewValue is bool))
            {
                return;
            }

            if ((bool)args.NewValue)
            {
                control.Loaded += (sender, e) =>
                    control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
            }
        }
    }
}

다음과 같이 사용하십시오.

<Window xmlns:Behaviors="clr-namespace:UI.Behaviors"
        Behaviors:FocusBehavior.FocusFirst="true">

6
제 생각에는,이는 지금까지 내가 찾은 최고의 솔루션입니다. 감사!
Shion

1
에 대한 호출에서이 답변의 코드에 버그가 있습니다 DependencyProperty.RegisterAttached. 세 번째 매개 변수는 typeof(FocusBehavior)아니 어야합니다 typeof(Control). 이 변경을 수행하면 디자이너가 'Control'오류로 이미 등록 된 'FocusFirst'속성을보고하지 못합니다.
Tony Vitabile

@TonyVitabile 수정. 가능하면 언제든지 자유롭게 답변을 편집하고 개선 할 수 있습니다. :)
Mizipzor

언로드하는 동안로드 된 이벤트 처리기를 등록 취소해야합니까?
andreapier

@andreapier 관심이 있다면 등록을 건너 뛰어도 메모리 누수가 발생하지는 않습니다. 수명이 짧은 객체에 수명이 긴 객체의 이벤트에 메서드가 연결되어있는 경우 메모리 누수를 일으키는 이벤트에 대해서만 걱정하면됩니다. 이 경우 수명은 창의 수명이므로 괜찮습니다.
Joe White

14

다른 가능한 해결책을 찾았습니다. Mark Smith는 FocusManager.FocusedElement와 함께 사용할 FirstFocusedElement 태그 확장 을 게시했습니다 .

<UserControl x:Class="FocusTest.Page2"
    xmlns:FocusTest="clr-namespace:FocusTest"
    FocusManager.FocusedElement="{FocusTest:FirstFocusedElement}">

완전히 매끈하다! 감사합니다!
Andy

9

동일한 문제로 간단한 해결책으로 해결했습니다. 메인 창에서 :

  <Window ....
        FocusManager.FocusedElement="{Binding ElementName=usercontrolelementname}"
         ... />

사용자 컨트롤에서 :

private void UserControl_GotFocus_1(object sender, RoutedEventArgs e)
        {
            targetcontrol.Focus();
            this.GotFocus -= UserControl_GotFocus_1;  // to set focus only once
        }

3
컨트롤이 UserControl 내에 중첩되어 있지 않은 경우 컨트롤이 Window 안에 직접있는 경우에만 작동합니다.
Joe White

8

'WPF 초기 초점 악몽'을 겪고 스택에 대한 답변을 바탕으로 다음이 가장 좋은 솔루션임을 입증했습니다.

먼저 App.xaml OnStartup ()을 다음과 같이 추가하십시오.

EventManager.RegisterClassHandler(typeof(Window), Window.LoadedEvent,
          new RoutedEventHandler(WindowLoaded));

그런 다음 App.xaml에도 'WindowLoaded'이벤트를 추가하십시오.

void WindowLoaded(object sender, RoutedEventArgs e)
    {
        var window = e.Source as Window;
        System.Threading.Thread.Sleep(100);
        window.Dispatcher.Invoke(
        new Action(() =>
        {
            window.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));

        }));
    }

일부 프레임 워크 경쟁 조건으로 인해 WPF 초기 초점이 대부분 실패하므로 스레딩 문제를 사용해야합니다.

전체 앱에 대해 전 세계적으로 사용되는 다음 솔루션을 가장 잘 찾았습니다.

그것이 도움이되기를 바랍니다 ...

오란


5
BeginInvoke그 무서운 Sleep(100)진술 대신 사용하십시오 .
l33t

8

XAML에서 컨트롤 자체를 포커스 된 요소로 쉽게 설정할 수 있습니다.

<Window>
   <DataGrid FocusManager.FocusedElement="{Binding RelativeSource={RelativeSource Self}}">
     ...
   </DataGrid>
</Window>

나는 이것을 usercontrol에서 설정하고 이것이 작동하는지 보지 않았지만 그것이 가능할 수도 있습니다.


초점 문제에 대해서만 컨트롤의 이름을 지정하지 않았기 때문에 흥미롭게 들립니다. 반면에 사용자 컨트롤을 사용한 테스트가 작동하지 않았습니다.
heringer

그것은 @heringer를 놀라게하지 않습니다 ... 그것은 <border> 또는 비슷한 비 대화 형 컨트롤에 초점을 맞추는 것과 같습니다. 이 FocusedElement 속성을 usercontrol 내부의 대화식 컨트롤에 적용 해 볼 수 있습니다. 그러나 이것은 옵션이 아닐 수 있습니다.
Simon Gillbee

스택 패널 에서이 방법을 사용하여 양식이로드되면 집중하려는 하위 버튼을 설정했습니다. 정말 감사합니다
Julio Nobre

바인딩이 완전히 끊어 질 수 있으므로주의하십시오. stackoverflow.com/questions/30676863/…
Der_Meister

2

C # 6+에 대한 Mizipzor의 최소 버전 .

public static class FocusBehavior
{
    public static readonly DependencyProperty GiveInitialFocusProperty =
        DependencyProperty.RegisterAttached(
            "GiveInitialFocus",
            typeof(bool),
            typeof(FocusBehavior),
            new PropertyMetadata(false, OnFocusFirstPropertyChanged));

    public static bool GetGiveInitialFocus(Control control) => (bool)control.GetValue(GiveInitialFocusProperty);
    public static void SetGiveInitialFocus(Control control, bool value) => control.SetValue(GiveInitialFocusProperty, value);

    private static void OnFocusFirstPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var control = obj as Control;

        if (control == null || !(args.NewValue is bool))
            return;

        if ((bool)args.NewValue)
            control.Loaded += OnControlLoaded;
        else
            control.Loaded -= OnControlLoaded;
    }

    private static void OnControlLoaded(object sender, RoutedEventArgs e) => ((Control)sender).MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
}

XAML에서 사용 :

<Window local:FocusBehavior.GiveInitialFocus="True" />

1

당신이 나와 같고 어떻게 든 기본 초점 동작을 망치고 위의 모든 솔루션을 관련시키지 않는 프레임 워크를 사용하는 경우 여전히이 작업을 수행 할 수 있습니다.

1-초점을 얻는 요소를 적어 둡니다 (무엇이든)

2-xxx.xaml.cs 뒤에 코드에 추가하십시오.

private bool _firstLoad;

3-첫 번째 초점을 얻는 요소에 이것을 추가하십시오.

GotFocus="Element_GotFocus"

4-코드 뒤에 Element_GotFocus 메소드를 추가하고 첫 번째 초점이 필요한 WPF라는 요소를 지정하십시오.

private void Element_GotFocus(object sender, RoutedEventArgs e)
{
    if(_firstLoad)
    {
        this.MyElementWithFistFocus.Focus();
        _firstLoad = false;
    }
}

5-로드 된 이벤트 관리

XAML에서

Loaded="MyWindow_Loaded"   

xaml.cs에서

private void MyWindow_Loaded(object sender, RoutedEventArgs e)
{
        _firstLoad = true;
        this.Element_GotFocus(null, null);
}

이것이 최후의 수단으로 도움이되기를 바랍니다.


0

나는 또한 같은 문제에 직면했다. 캔버스 컨테이너 안에 세 개의 텍스트 상자가 있었고 사용자 정의 컨트롤이 열릴 때 첫 번째 텍스트 상자에 초점을 맞추기를 원했습니다. WPF 코드는 MVVM 패턴을 따랐습니다. 나는 요소에 초점을 맞추기 위해 별도의 행동 클래스를 만들고 이것을 내 견해에 묶었 다.

캔버스 비헤이비어 코드

public  class CanvasLoadedBehavior : Behavior<Canvas>
{
    private Canvas _canvas;
    protected override void OnAttached()
    {
        base.OnAttached();
        _canvas = AssociatedObject as Canvas;
        if (_canvas.Name == "ReturnRefundCanvas")
        {

            _canvas.Loaded += _canvas_Loaded;
        }


    }

    void _canvas_Loaded(object sender, RoutedEventArgs e)
    {
        FocusNavigationDirection focusDirection = FocusNavigationDirection.Next;

        // MoveFocus takes a TraveralReqest as its argument.
        TraversalRequest request = new TraversalRequest(focusDirection);
        UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;
        if (elementWithFocus != null)
        {
            elementWithFocus.MoveFocus(request);
        }

    }

}

볼 코드

<Canvas  Name="ReturnRefundCanvas" Height="200" Width="1466" DataContext="{Binding RefundSearchViewModel}">
                <i:Interaction.Behaviors>
                    <b:CanvasLoadedBehavior />
                </i:Interaction.Behaviors>
                <uc:Keyboard Canvas.Left="973" Canvas.Top="111" ToolTip="Keyboard" RenderTransformOrigin="-2.795,9.787"></uc:Keyboard>
                <Label  Style="{StaticResource Devlbl}" Canvas.Left="28" Content="Return and Refund Search" Canvas.Top="10" />
                <Image Height="30" Width="28" Canvas.Top="6" Canvas.Left="5" Source="pack://application:,,,/HomaKiosk;component/images/searchF.png">
                    <Image.OpacityMask>
                        <ImageBrush ImageSource="pack://application:,,,/HomaKiosk;component/images/searchF.png"/>
                    </Image.OpacityMask>
                </Image>

                <Separator Height="4" Canvas.Left="6" Margin="0" Canvas.Top="35" Width="1007"/>

                <ContentControl Canvas.Top="45" Canvas.Left="21"
                    ContentTemplate="{StaticResource ErrorMsg}"
                    Visibility="{Binding Error, Converter={c:StringNullOrEmptyToVisibilityConverter}}" 
                    Content="{Binding Error}" Width="992"></ContentControl>

                <Label  Style="{StaticResource Devlbl}" Canvas.Left="29" Name="FirstName" Content="First Name" Canvas.Top="90" />
                <wpf:AutoCompleteTextBox  Style="{StaticResource AutoComp}" Height="32" Canvas.Left="33" ToolTip="First Name"  Canvas.Top="120" Width="205"                     Padding="10,5" TabIndex="1001"
                    VerticalAlignment="Top"

                    Watermark=""
                    IconPlacement="Left"
                    IconVisibility="Visible"
                    Delay="100"

                    Text="{Binding FirstName, Mode=TwoWay, TargetNullValue=''}" 
                    Provider="{Binding FirstNameSuggestions}">
                    <wpf:AutoCompleteTextBox.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="5">
                                <StackPanel Orientation="Vertical">
                                    <TextBlock Text="{Binding}"
                   FontWeight="Bold" />
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </wpf:AutoCompleteTextBox.ItemTemplate>
                </wpf:AutoCompleteTextBox>

                <Label Style="{StaticResource Devlbl}" Canvas.Left="250" Content="Last Name" Canvas.Top="90" />
                <wpf:AutoCompleteTextBox  Style="{StaticResource AutoComp}" Height="32" ToolTip="Last Name" Canvas.Left="250"  Canvas.Top="120" Width="205" Padding="10,5"  TabIndex="1002"
                    VerticalAlignment="Top"
                    Watermark=""
                    IconPlacement="Left"
                    IconVisibility="Visible"
                    Delay="100"
                   Text="{Binding LastName, Mode=TwoWay, TargetNullValue=''}" 
                    Provider="{Binding LastNameSuggestions}">
                    <wpf:AutoCompleteTextBox.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="5">
                                <StackPanel Orientation="Vertical">
                                    <TextBlock Text="{Binding}"
                   FontWeight="Bold" />
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </wpf:AutoCompleteTextBox.ItemTemplate>
                </wpf:AutoCompleteTextBox>

                <Label Style="{StaticResource Devlbl}" Canvas.Left="480" Content="Receipt No" Canvas.Top="90" />
                             <wpf:AutoCompleteTextBox  Style="{StaticResource AutoComp}" Height="32" ToolTip="Receipt No" Canvas.Left="480"  Canvas.Top="120" Width="205" Padding="10,5"  TabIndex="1002"
                    VerticalAlignment="Top"
                    Watermark=""
                    IconPlacement="Left"
                    IconVisibility="Visible"
                    Delay="100"
                    Text="{Binding ReceiptNo, Mode=TwoWay, TargetNullValue=''}" 
                    Provider="{Binding ReceiptIdSuggestions}">
                    <wpf:AutoCompleteTextBox.ItemTemplate>
                        <DataTemplate>
                            <Border Padding="5">
                                <StackPanel Orientation="Vertical" >
                                    <TextBlock Text="{Binding}"
                   FontWeight="Bold">

                                    </TextBlock>
                                </StackPanel>
                            </Border>
                        </DataTemplate>
                    </wpf:AutoCompleteTextBox.ItemTemplate>
                    <i:Interaction.Behaviors>
                        <b:AllowableCharactersTextBoxBehavior RegularExpression="^[0-9]+$" MaxLength="15" />
                    </i:Interaction.Behaviors>
                </wpf:AutoCompleteTextBox>
                <!--<Label Style="{StaticResource Devlbl}" Canvas.Left="710" Content="Duration" Canvas.Top="79" />-->
                <!--<ComboBox AllowDrop="True" Canvas.Left="710" ToolTip="Duration" Canvas.Top="107" Width="205" TabIndex="1004"
                    Style="{StaticResource CommonComboBox}"      
                    ItemsSource="{Binding Durations}" DisplayMemberPath="Description" SelectedValuePath="Id" SelectedValue="{Binding SelectedDate, Mode=TwoWay}">

                </ComboBox>-->

                <Button Content="Search" Style="{StaticResource MyButton}" ToolTip="Search" 
                    Canvas.Top="116" Canvas.Left="710" Cursor="Hand" 
                    Command="{Binding SearchCommand}" TabIndex="2001">
                </Button>
                <Button Content="Clear" Style="{StaticResource MyButton}"  ToolTip="Clear"
                    Canvas.Top="116" Canvas.Left="840" Cursor="Hand" 
                    Command="{Binding ClearCommand}" TabIndex="2002">
                </Button>
                <Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="25" Source="pack://application:,,,/HomaKiosk;component/images/chkpending.png"/>
                <Label  Style="{StaticResource LegendLbl}" Canvas.Left="50" Content="Check Returned and Payment Pending" Canvas.Top="178" />
                <Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="300" Source="pack://application:,,,/HomaKiosk;component/images/chkrepaid.png"/>
                <Label  Style="{StaticResource LegendLbl}" Canvas.Left="325" Content="Repaid" Canvas.Top="178" />
                <Image Height="25" Width="25" Canvas.Top="175" Canvas.Left="395" Source="pack://application:,,,/HomaKiosk;component/images/refund.png"/>
                <Label  Style="{StaticResource LegendLbl}" Canvas.Left="415" Content="Refunded" Canvas.Top="178" />
                 </Canvas>

0

위의 솔루션이 예상대로 작동하지 않았으므로 Mizipzor가 제안한 동작을 다음과 같이 약간 변경했습니다.

이 부분에서

if ((bool)args.NewValue)
        {
            control.Loaded += (sender, e) =>
                   control.MoveFocus(new TraversalRequest(FocusNavigationDirection.Next));
        }

이에

if ((bool)args.NewValue)
        {
            control.Loaded += (sender, e) => control.Focus();
        }

ANd이 동작을 Window 또는 UserControl에 첨부하지 않지만 처음에는 다음과 같이 초점을 맞추고 싶습니다.

<TextBox ui:FocusBehavior.InitialFocus="True" />

아, 다른 이름으로 인해 죄송합니다. 연결된 속성에 InitialFocus 이름을 사용하고 있습니다.

그리고 이것은 나를 위해 일하고 있습니다. 아마 다른 사람을 도울 수 있습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.