programing

WPF 및 초기 초점

subpage 2023. 5. 4. 19:58
반응형

WPF 및 초기 초점

WPF 애플리케이션이 시작되면 아무것도 초점을 맞추지 못하는 것 같습니다.

이거 진짜 이상해요.제가 사용한 다른 모든 프레임워크는 여러분이 예상하는 대로 수행합니다. 탭 순서의 첫 번째 컨트롤에 초기 초점을 맞춥니다.하지만 제 앱뿐만 아니라 WPF라는 것을 확인했습니다. 새 창을 만들고 텍스트 상자를 넣고 앱을 실행하면 텍스트 상자를 클릭하거나 Tab을 누를 때까지 텍스트 상자에 포커스가 없습니다.우웩.

제 실제 앱은 단순한 텍스트 상자 이상으로 복잡합니다.사용자 컨트롤 내에 여러 계층의 사용자 컨트롤이 있습니다.이러한 사용자 컨트롤 중 하나에 Focusable=이 있습니다."True" 및 KeyDown/KeyUp 핸들러를 선택하면 창이 열리면 바로 포커스가 잡히도록 합니다.하지만 저는 아직 WPF 초보자이고, 이것을 하는 방법을 생각하는 것은 그다지 운이 좋지 않습니다.

앱을 시작하고 Tab 키를 누르면 포커스가 포커스 가능한 컨트롤로 이동하고 원하는 대로 작동합니다.그러나 사용자가 창을 사용하기 전에 Tab을 눌러야 하는 것은 원하지 않습니다.

저는 포커스 매니저를 가지고 놀았습니다.FocusedElement(초점 요소), 그러나 어떤 컨트롤에 설정해야 할지 잘 모르겠습니다(최상위 창).포커스 가능한 컨트롤을 포함하는 부모?포커스 가능한 컨트롤 자체?) 또는 무엇으로 설정해야 합니다.

창이 열리자마자 심층적인 제어 기능이 초기 초점을 맞추려면 어떻게 해야 합니까?아니면 탭 순서에서 첫 번째 초점 컨트롤에 초점을 맞추는 것이 더 낫습니까?

이 기능도 작동합니다.

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

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

저는 Reflector를 통해 Focusable 속성이 어디에 사용되는지 확인할 수 있는 좋은 아이디어를 얻었고 이 솔루션에 대한 방법을 찾았습니다.다음 코드를 Windows 생성자에 추가하기만 하면 됩니다.

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

이렇게 하면 탭 순서의 첫 번째 컨트롤이 자동으로 선택되므로 모든 창에 놓을 수 있고 작업만 수행할 수 있는 일반적인 솔루션입니다.

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

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">

저는 다른 가능한 해결책을 찾았습니다.Mark Smith는 FirstFocusElement 마크업 확장 기능을 FocusManager에 게시했습니다.집중 요소.

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

'WPF 초기 집중 악몽'을 꾸고 스택에 대한 몇 가지 답변을 토대로 다음과 같은 내용이 제게 최고의 솔루션임을 증명했습니다.

먼저 App.xaml OnStartup()을 추가합니다.

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

그런 다음 App.xaml에서도 'Windows Loaded' 이벤트를 추가합니다.

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 초기 포커스가 일부 프레임워크 경합 조건으로 인해 대부분 실패하므로 스레드 문제를 사용해야 합니다.

저는 다음 솔루션이 전체 앱에 전 세계적으로 사용되기 때문에 가장 적합하다고 생각했습니다.

도움이 되길...

오란

간단한 솔루션으로 동일한 문제를 해결했습니다.기본 창에서:

  <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
        }

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

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

사용자 컨트롤에서 이 기능을 설정하고 이 기능이 작동하는지 확인해 본 적은 없지만 그럴 수도 있습니다.

Mizipzor의 C#6+에 대한 답변의 최소 버전입니다.

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" />

위의 해결책은 제가 예상했던 것처럼 작동하지 않았습니다. 저는 다음과 같이 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();
        }

그리고 이 동작을 Window(윈도우)나 UserControl(사용자 제어)에 첨부하는 것이 아니라 제어를 위해 처음에는 다음과 같이 초점을 맞추고 싶습니다.

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

아, 다른 이름을 지정해서 죄송합니다. 첨부된 속성에 대해 InitialFocus 이름을 사용하고 있습니다.

그리고 이것은 저에게 효과가 있습니다. 아마도 다른 사람을 도울 수 있을 것입니다.

만약 당신이 나와 같고, 어떻게든 기본적인 초점 행동을 엉망으로 만들고, 위의 모든 해결책을 상관없이 만드는 몇 가지 프레임워크를 사용하고 있다면, 당신은 여전히 이것을 할 수 있습니다.

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);
}

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

저도 같은 문제에 직면했습니다.캔버스 컨테이너 안에 3개의 텍스트 상자가 있었고 사용자 컨트롤이 열릴 때 첫 번째 텍스트 상자의 초점이 맞춰지기를 원했습니다.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>
<Window FocusManager.FocusedElement="{Binding ElementName=yourControlName}">

언급URL : https://stackoverflow.com/questions/817610/wpf-and-initial-focus

반응형