안녕하세요.
저번에 가로 스크롤 관련 도움이 정말 큰 도움이 되었습니다.
감사합니다.
3월달에도 이런 오류가 있어서, command 로 처리한기억이 있는데
이번에 또 다시 발생하게되어 도움이 필요한 상황입니다.
오늘 하루 종일 검색하고, 디버그해도 뾰족한 해결책이 나오지 않네요 ㅠ
Microsoft.Xaml.Behaviors.Wpf 사용중이며
최초 오류는
<ComboBox ItemsSource="{Binding ESetCameraPropertyList}"
SelectedItem="{Binding SelectedESetCameraProperty}"
Margin="10"
Width="Auto">
<b:Interaction.Triggers>
<b:EventTrigger EventName="SelectionChanged">
<b:InvokeCommandAction Command="{Binding SelectionChangedCommand}"
PassEventArgsToCommand="True" />
</b:EventTrigger>
</b:Interaction.Triggers>
</ComboBox>
Triggers 사용때 발견 되었습니다.
설명의 편의를 위해 구현은 1번Page에하였다고 가정 하겠습니다.
최초 1번 페이지 로드후 이벤트 등록
2번페이지 이동 → 다시 1번페이지로 이동시
이벤트가 다시 등록 되어,
트리거 작동시 페이지이동횟수 x 2 의 횟숫만큼 작동을 하였습니다.
즉 1번만 발동 해야될 이벤트헨들러 메소드가 페이지를 이동이후 2회, 4회, 6회 … 10회 이런식으로 계속 늘어났습니다. (페이지 이동 없이처음부터 1회발동이 아닌 2회발동)
이게 최초 문제를 인식했을때이고, 헨들러가 여러번 등록 되고 있다는 생각은 하였지만 커맨드만으로 구현하여 넘어갔던 부분이었습니다.
다음오류
public class VirtualKeyboardBehavior : Behavior<TextBox>
{
private readonly object _lock = new object();
private Process _keyboardProcess;
private bool _isFocused;
// private bool _isEventHandlersAttached;
protected override void OnAttached()
{
// base.OnAttached();
Log.Information("온어테치 매서드 실행");
AssociatedObject.GotFocus += AssociatedObject_GotFocus;
AssociatedObject.LostFocus += AssociatedObject_LostFocus;
Application.Current.Deactivated += Current_Deactivated;
Application.Current.Activated += Current_Activated;
}
protected override void OnDetaching()
{
Log.Information("온디테치 매서드 실행");
AssociatedObject.GotFocus -= AssociatedObject_GotFocus;
AssociatedObject.LostFocus -= AssociatedObject_LostFocus;
Application.Current.Deactivated -= Current_Deactivated;
Application.Current.Activated -= Current_Activated;
base.OnDetaching();
}
private void AssociatedObject_GotFocus(object sender, RoutedEventArgs e)
{
_isFocused = true;
ShowKeyboard();
}
private void AssociatedObject_LostFocus(object sender, RoutedEventArgs e)
{
_isFocused = false;
CloseKeyboard();
Log.Information("LostFocus");
}
private void Current_Deactivated(object? sender, EventArgs e)
{
CloseKeyboard();
Log.Information("Deactivated");
if (_keyboardProcess != null) Log.Information("키보드를 닫았으나 닫기지 않음," + _keyboardProcess.Id);
}
private void Current_Activated(object? sender, EventArgs e)
{
if(_isFocused) ShowKeyboard();
Log.Information("Activated");
}
private void ShowKeyboard()
{
if (_keyboardProcess == null)
{
var path64 =
Path.Combine(
Directory.GetDirectories(
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Windows), "winsxs"),
"amd64_microsoft-windows-osk_*")[0], "osk.exe");
var path32 = @"C:\windows\system32\osk.exe";
var path = Environment.Is64BitOperatingSystem ? path64 : path32;
if (File.Exists(path))
{
try
{
var startInfo = new ProcessStartInfo(path)
{
UseShellExecute = true,
Verb = ""
};
_keyboardProcess = Process.Start(startInfo);
Log.Information("키보드 실행 - 프로세스 ID :" + _keyboardProcess.Id);
}
catch (Win32Exception ex)
{
Console.WriteLine("관리자 권한으로 실행할 수 없습니다: " + ex.Message);
}
}
else
{
Console.WriteLine("파일을 찾을 수 없습니다: " + path);
}
}
}
private void CloseKeyboard()
{
lock (_lock)
{
Process[] processes = Process.GetProcessesByName("osk");
if (processes.Length > 0)
{
// The keyboard process is already running
_keyboardProcess = processes[0];
}
if (_keyboardProcess != null)
{
Log.Information("키보드 실행 중지 - 프로세스 ID :" + _keyboardProcess.Id);
_keyboardProcess.Kill();
_keyboardProcess = null;
}
}
}
}
일단 behavior 부터 보시죠
TextBox 활성화시 가상 키보드를 실행하는 behavior 입니다.
페이지를 로드 하면, 시작부터 OnAttached() 가 2번 실행 됩니다.
액티베이트와 디 액티베이트도 2번씩 실행됩니다.
여기서 한가지 다른점은,
포커스는 1번만 실행 된다는 점입니다.
처음 OnAttached() 로그가 2번 찍히는것도 이해가 되질 않지만,
Focus 는 한번만 찍히는 부분도 이해가 힘듭니다.
혹시나 뷰모델이 싱글톤으로 관리되는게 문제인가 하여, 변경 해 보았지만 별 소득이 없었습니다.
private IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// 옵션 AddTransient(요청이 있을때마다 새로 인스턴스 생성),
// AddSingleton(싱글톤),
// AddScoped(요청의 생명 주기 동안 하나의 인스턴스를 생성, 한 요청 내에서는 같은 인스턴스가 재사용되지만, 새로운 요청이 들어오면 새로운 인스턴스가 생성)
// Service 등록
services.AddSingleton<RuntimeCommandService>();
services.AddSingleton<SystemSettingService>();
// ViewModel 등록
services.AddTransient<MainViewModel>();
services.AddSingleton<HomeViewModel>();
services.AddSingleton<CustomerViewModel>();
services.AddSingleton<StreamViewModel>();
services.AddSingleton<SettingViewModel>();
// SnakbarMessageQueue 등록
services.AddSingleton<SnackbarMessageQueue>();
// initService 는 SystemLogViewModel 클래스의 인스턴스를 생성하여 서비스에 등록
// 시작과 동시에 인스턴스화, 로그페이지는 시작과 동시에 로그를 받아야함
services.AddSingleton(new SystemLogViewModel());
뷰모델을 전부 Singleton 혹은 Transient 로 변경해보았지만 결과는 같았습니다.
다른 behavior 도 같은 현상일까 또 테스트 해 보았습니다.
public class TextBlockNavigate : Behavior<TextBlock>
{
private bool _isLocated;
protected override void OnAttached()
{
// base.OnAttached();
Log.Information("로그창 온어테치 매서드 실행");
AssociatedObject.PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown;
}
protected override void OnDetaching()
{
Log.Information("로그창 디페치드 매서드 실행");
AssociatedObject.PreviewMouseLeftButtonDown -= OnPreviewMouseLeftButtonDown;
base.OnDetaching();
}
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2 && _isLocated == false)
{
WeakReferenceMessenger.Default.Send(new NavigationMessage("Views/SystemLogPage.xaml"));
_isLocated = true;
}
else if (e.ClickCount == 2)
{
WeakReferenceMessenger.Default.Send(new NavigationMessage("GoBack"));
_isLocated = false;
}
e.Handled = true;
}
}
TextBlock 을 클릭하게되면 페이지를 이동시키는 Behavior 입니다.
이부분은
OnAttached() 가 한번만 호출 되는걸 확인하였습니다.
더 혼란스러워졌습니다…
혹시나, View 에 TextBox 가 2개가 존재하는데, 두군데 다 behavior 인터렉션을 작성해놔서 그런가 하여, 하나로 바꾸어 실행
<TextBox
InputScope=""
VerticalAlignment="Center"
materialDesign:TextFieldAssist.HasClearButton="True"
materialDesign:HintAssist.Hint="{x:Static properties:Resources.setValue}"
Width="Auto"
MaxWidth="200"
Text="{Binding MessageInput, UpdateSourceTrigger=PropertyChanged}"
Margin="10"
TextWrapping="Wrap"
>
<b:Interaction.Behaviors>
<behaviors:VirtualKeyboardBehavior />
</b:Interaction.Behaviors>
</TextBox>
<Button
Width="auto"
Command="{Binding SendCommand}"
Margin="10,10,10,10">
<materialDesign:PackIcon
Kind="Send"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Button>
<TextBox
VerticalAlignment="Center"
materialDesign:TextFieldAssist.HasClearButton="True"
materialDesign:HintAssist.Hint="{x:Static properties:Resources.setPortNumber}"
ToolTip="{x:Static properties:Resources.settingPortToolTip}"
Width="Auto"
MaxWidth="200"
Text="{Binding PortInput, UpdateSourceTrigger=PropertyChanged}"
Margin="10"
TextWrapping="Wrap">
<b:Interaction.Behaviors>
<behaviors:VirtualKeyboardBehavior />
</b:Interaction.Behaviors>
</TextBox>
매번 2번 호출 되던 OnAttached() 가 1번만 호출 되는것을 확인 하였습니다.
2번 호출 되는 문제는 찾았지만, 페이지 이동시 이벤트 헨들러가 중복으로 등록 되는 문제는 찾지 못하였습니다.
2번 등록되는 문제는 찾았을 지언정, 텍스트박스마다 Behavior 인터렉션을 적용 하지 않으면 Behavior 의 재사용은 어떻게 하는건지,
페이지 이동시 OnAttached() 는 호출이 되고 OnDetaching() 는 호출이 되지 않는건지, Behavior 의 재사용은 요소마다 인터렉션을 걸어주는게 아닌건지.
정리가 좀 안되는데, 다시 정리 하자면
1. OnAttached() 가 View 의 인터렉션을 걸어준 수만큼 호출된다.
2. OnAttached() 는 페이지 이동시 지속적으로 호출이 되는데, OnDetaching()는 페이지를 이동하여도 호출이 되지 않는다, 결과적으로 페이지 이동시 헨들러가 중복으로 쌓이게 된다. 그결과 본래의 의도는 한번의 행동에 한번의 메서드 호출이 → 한번의 행동으로 n번의 메서드 호출을 야기한다. (여기서n은 페이지 이동횟수)
3. 2번의 호출을 야기한문제는 view 에서 behavior 인터렉션을 2번 작성해 준것에서 나왔는데, 모든 텍스트 박스에서 behavior 을 구현 하고자 하면 어떻게 해야 하는것이 옳은 것인가?
여기까지 입니다.
글이 많이 길지만 읽어주셔서 감사합니다.
많은 도움과 지도 부탁드리겠습니다.