3월, 2018의 게시물 표시

[C#/WPF] TextBox.Text 에 Binding된 속성이 즉시 업데이트되지 않는 경우

포커스를 잃을 때만 업데이트되는 TextBox.Text Binding   방금 TextBox.Text에 따라 WebBrowser.NavigateToString()을 호출하려 하는 중, 괜히 Binding을 쓰고 싶어서 TextBox.Text에 Binding Html을 걸어놓고 디버그를 해보니 텍스트가 입력될 때가 아니라, 텍스트박스의 포커스를 잃을 때 Html 프로퍼티가 업데이트되는 현상 을 발견했습니다.   제 목적은 실시간으로 WebBrowser에 TextBox에 입력된 HTML을 렌더링하는 작업이기 때문에 매우 곤란한 상황이죠.   하지만 언제나 그렇듯, 답은 매우 간단했습니다. Binding.UpdateSourceTrigger 프로퍼티   Binding 클래스에는 UpdateSourceTrigger 라는 프로퍼티가 있습니다. 이름에서 알 수 있듯, 언제 원본의 값을 업데이트할지 결정하는 녀석이죠. 기본값은 Default이며 Default는 Binding이 적용될 DependencyProperty를 만들 때 설정한 기본값 으로 동작합니다.   Binding.UpdateSourceTrigger 프로퍼티에 들어갈 수 있는 값은 UpdateSourceTrigger 열거형 의 값들인데요, 아래와 같습니다. public enum UpdateSourceTrigger { Default, PropertyChanged, LostFocus, Explicit, }   Default 는 위에서 말씀드렸듯, DependencyProperty를 만들 때 설정한 기본값입니다.   PropertyChanged 는 해당 DependencyProperty에 변경이 생길 때 원본을 업데이트합니다.   LostFocus 는 DependencyProperty를 지닌 컨트롤이 포커스를 잃을 때 원본을 업데이트합니다.   Explicit 은 원본을 업데이트하기 위해 BindingExpression.UpdateSour

[C#/WPF] LambdaConverters, Binding 컨버터를 람다로 손쉽게 만들어보자

이미지
  이 동영상에서는 LambdaConverters 를 사용해 IValueConverter를 간단하게 람다식으로 구현하는 방법에 대해 설명합니다.   핵심 내용만 정리하면 아래와 같습니다. // 람다 컨버터 제작 public static class HsConverters { // One-way Binding public static readonly IValueConverter PresentConverter = ValueConverter.Create (b => str.Value.Equals("있음")); // Two-way Binding public static readonly IValueConverter PresentConverterTwoWay = ValueConverter.Create (b => str.Value.Equals("있음"), str => str.Value.Equals("있음")); // One-way To Source Binding public static readonly IValueConverter PresentConverterOneWayToSource = ValueConverter.Create (null, str => str.Value.Equals("있음")); } <!-- XAML --> <TextBox Text="{Binding BoolValue, Converter={x:Static myNamespace:HsConverters.PresentConverterTwoWay}}" />

[컴퓨터 관리] 죽어가는 그래픽카드 쿨러를 소생시킨 뿌리는 그리스

이미지
가혹한 혹사로 죽어가는 VGA 쿨러   작년 배틀그라운드가 출시되고, GTX980의 힘을 빌려 친구들과 꾸준히 플레이를 해왔습니다. 그러나 헬적화에 이름에 걸맞는 프레임 드랍, 특정 지역만 가면 더 심해지는 엄청난 프레임 드랍을 견디지 못하고 AORUS GRAPHICS ENGINE을 사용해 오버클럭을 해 플레이했죠.   오버클럭 자체는 문제가 없었습니다. 다만, 수율 조정을 위해 한계 온도를 기본값인 79℃에서 90℃ 로 올려서 사용했던 것이 문제가 된 것 같습니다. (확실하진 않음)   배그는 세 달 전 하드캐리머신이 군대를 가서 접었고, 이후 히오스나 게리모드를 주로 플레이했는데, 평소보다 훨씬 심한 프레임 드랍에 뚝뚝 끊기는 현상이 자주 발생해 HWMonitor로 확인한 결과, 쿨러 속도가 평균 1200RPM => 250~260RPM 으로 급감해있는 것을 목격했습니다.   기겁한 저는 당장 케이스를 열어 쿨러의 상태를 확인했는데, 총 3개의 쿨러 중 1개는 완전히 멈추고 , 한 개는 눈으로 돌아가는 속도를 잴 수 있을 만큼 느려졌고, 멀쩡한 놈은 한 개 뿐 이었습니다.   당연히 쿨러의 속도가 떨어지니 온도가 올라가고, 안전 장치로 클럭이 떨어져 코어 클럭이 1357MHz => 120~240MHz 로 떨어진 채 가동중이었죠. 해결 방안 모색   해결 방법을 찾아야 했습니다. 구글에 VGA 쿨러를 살릴 방법을 검색해 봤더니 WD40을 뿌려라, 미싱기름을 떨궈라 같은 미친 개소리가 난무해서 거르고, 예전 어느 한 유튜버가 베어링으로 피젯 스피너를 만들 때 베어링 내부의 구슬에 그리스가 덕지덕지 발라져 있던 모습을 떠올려 그리스를 구매하고자 했는데...   문제는 제 기가바이트사의 980의 쿨러는 전용 드라이버(흔히들 맥가이버 키트라고 불리는 키트에 들어있는 작은 드라이버)가 없으면 뜯을 수가 없게 돼있었습니다. 나사가 너무 작아 집에 있는 두 종류의 드라이버 모두 사용할 수 없었고, 무엇보다 당장

[C#/WPF] Window의 크기를 테두리가 아닌 내부 영역 기준으로 설정하는 방법 (사이즈에 맞추는 방법과 동일)

이미지
  WPF를 가지고 놀다 보면 불편한 점이 하나 있습니다. WinForm으로 코딩할 땐 그냥 디자이너나 InitializeComponent() 이후 ClientSize를 설정하면 윈도우 버전/테마마다 다른 테두리 두께와 상관없이 렌더링 영역의 크기를 설정할 수 있었는데, WPF에서는 그렇다 할 프로퍼티가 보이지 않고, 있더라도 불편하게 많은 줄의 코드를 추가해야 한다는 것이죠.   하지만, 약간의 꼼수만 있다면 언제나 그렇든 매우 간편하게 해결할 수 있습니다. 렌더링 영역이 16:9인 가로 120의 윈도우를 만들고 싶다   16:9 = 120:67.5 이므로 세로를 68로 설정해야 합니다. 우선, 항상 하듯 Window 의 Width 와 Height 를 120과 68로 설정해볼까요?   보시다시피 창 자체의 크기 가 120x68로 설정되었고, 렌더링되는 내부의 크기는 그보다 작게 설정 되었습니다.   뭐, 스택오버플로우나 이곳저곳 돌아다니다 보면 제가 쓰려는 이 방법 말고도 코드 도입부에 4줄정도 추가해 내부 사이즈를 조정하는 방법이 있긴 합니다만, 너무 길고 귀찮으므로 간단하게 XAML 선에서 해결을 봅시다. Window의 Width , Height  속성을 제거한다. Window의 SizeToContent 속성을 SizeToContent.WidthAndHeight 로 설정한다. Window의 첫 번째 자식 요소의 Width 와 Height 를 고정으로 설정한다. (여기선 120, 68)   위와 같이 하면 일단 시작 시 클라이언트의 영역은 원한 대로 설정됩니다. (120*68)   그러나, 치명적인 문제 가 하나 생기는데요, 윈도우의 크기가 변해야 할 상황이 생기는 경우(리사이즈 등), 내부 엘리먼트가 고정 크기이기 때문에 변하지 않고 그대로 유지 됩니다.   다이얼로그 나 기타 고정된 사이즈의 윈도우 에는 위 3단계로 충분하지만, 그게 아니라면 아래와 같은 코드를 Window.Loaded 이벤트 핸들러에

[C#] WebClient.DownlaodFileAsync를 동기로 실행시키는 방법

  복수의 WebClient 객체에서 순서대로 다운로드해야만 하는 상황이 생겨서 찾던 중 이 블로그 에서 방법을 알게 돼 공유합니다. using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text.RegularExpressions; using System.Threading; namespace HsUpdaterWithSetup { class Program { private static readonly object _downloadLocker = new object(); static void Main(string[] args) { var projectName = "ABC"; Console.WriteLine("파일 목록 불러오는 중..."); var url = $"http://abc.def.com/path/{Uri.EscapeDataString(projectName)}/Release"; var req = WebRequest.CreateHttp(url); var fileList = new List<string>(); using (var res = req.GetResponse()) { using (var stream = res.GetResponseStream()) { using (var sr = new StreamReader(stream)) { var body = s

[C#/Selenium] IWebDriver.ExecuteJavascript 캐스팅이 잘못됐다는 에러가 뜰 경우

  아주 간단한 디버그로 원인 파악 및 해결이 가능합니다.   우선, 아래와 같은 코드가 있습니다. public static int GetPageOfSomePage(this IWebDriver driver) { return driver.ExecuteJavascript<int>(@" if (typeof(page) === 'undefined') return 0; return page.current; "); }   얼핏 봐서는 정상 작동할 것 같이 생겼지만, 런타임에 WebDriverException, 결과값은 리턴됐으나 캐스팅이 잘못됐다는 에러 를 뿜게 되죠..   일단 에러 설명에 결과값은 리턴됐다고 하므로, 디버그를 해 봅시다. public static object GetPageOfSomePage(this IWebDriver driver) { var obj = driver.ExecuteJavascript<object>(@" if (typeof(page) === 'undefined') return 0; return page.current; "); Debug.WriteLine(obj.GetType()); return obj; }   이제 런타임에 출력 창을 보면, System.Int64 또는 값에 따라서 System.Float, System.Double 등등이 보일 겁니다. 저 같은 경우는, System.Int64였죠.   캐스팅해야할 타입을 디버그로 알아냈으니 그대로 적용합니다. public static long GetPageOfSomePage(this IWebDriver driver) { return driver.ExecuteJavascript<long>(@" if (typeof(page) === 'undefine

[C#] UI 스레드에서 비동기 작업을 InvalidOperationException 없이 수행하는 방법

이전까지 썼던 비동기 코드(를 가장한 쓰레기)   멍청하게도, 저는 UI 코드에서 비동기 작업을 해야할 때 아래와 같은 식으로 작업했습니다. // MainWindow.cs public async void SomeElement_SomeEvent(object sender, RoutedEventArgs e) { await SomeMethod(); // SomeMethod() 내부에 비동기 구현이 되지 않았다면 await Task.Run(() => SomeMethod1()); }   근데 만약, SomeMethod 혹은 SomeMethod1 내부에 UI 스레드의 엘리먼트를 변경하는 코드가 있다면? public async Task SomeMethod() { _txtSomeElement.Text = "ABC"; }   해당 부분 코드가 실행될 경우, InvalidOperationException(크로스 스레드 작업이 잘못되었습니다) 라고 에러가 발생할 겁니다.   그래서, 아주 아주 멍청하게도 아래와 같은 방식의 코드를 짜서 썼었죠. public async void SomeElement_SomeEvent(object sender, RoutedEventArgs e) { await Dispatcher.Invoke(new Action(async () => await SomeMethod())); } public async void SomeMethod() { await Task.Run(() => _txtSomeElement.Text = "ABC"; } 세-상에... 저게 대체 무슨 가독성도 성능도 바닥에 말도 안 되는 코드란 말입니까! 설마, 그럴 리는 없겠지만, 저처럼 저게 왜 잘못됐는지 모르는 분이 계신다는 가정 하에 설명을 하도록 하겠습니다. Invoke, BeginInvoke   WinForms 앱에서는 Control.Invoke, Control

[C#/WPF] 객체의 속성을 Binding해서 사용하는 방법

  UserVo라는 로그인 정보를 담는 객체를 위한 클래스가 있습니다. UserId, UserPw, UserName 등 여러 유저 정보가 들어있죠.   회원가입 윈도우의 코드에는 이 UserVo 클래스의 객체가 UserVo라는 속성으로 정의돼 있습니다.   저는 이 속성 값을 그대로 Binding시켜 사용하려고 했습니다. 여기저기 구글링도 하고 스택오버플로우도 뒤져보고 했지만 키워드를 이상하게 선정했는지 작동하는 코드를 찾을 수 없었습니다.   그래서 혼자서 DataContext도 바꿔보고, Binding Source 및 Path도 바꿔보고 하다가 Source를 UserVo로, Path를 UserId, UserPw, UserName, ...으로 해서 일단 작동하게 하긴 했는데 아예 XAML에 몰아서 하는 방법 도 있어서 올립니다. // XAML <Window.Resources> <local:UserVo x:Key="UserVo" /> </Window.Resources> <!-- ... --> <TextBox Text={Binding Source={StaticResource UserVo}, Path={UserId} />   현재 윈도우 객체의 리소스에 UserVo 클래스의 인스턴스를 추가 하고, Binding할 때 Source를 리소스에 있는 인스턴스로, Path를 인스턴스의 프로퍼티명으로 설정하는 작업입니다. // MyWindow.cs public partial class MyWindow : Window { public UserVo UserVo => (UserVo) this.Resources["UserVo"]; // ... }   그리고 코드에서도 사용할 수 있게 public 한정자로 UserVo를 선언하고, x:Key 로 할당한 "UserVo"라는 이름의 리소스를 참조하게 합니다.   완성

[C#/WPF] StackPanel, DockPanel 등 특정 패널 내의 내용을 스크롤 할 수 있게 하는 방법

  ScrollViewer로 감싸면 됩니다. <ScrollViewer> <StackPanel> <!-- 스크롤을 벗어날 정도로 긴 콘텐츠 --> </StackPanel> </ScrollViewer>   물론, XAML 에디터에서, 혹은 문서 개요에서 ScrollViewer를 선택하면 여러 옵션을 조정할 수 있습니다.

[MySQL] Your password does not satisfy the current policy requirements 문제 발생시 해결 방법

1. 루트 권한으로 validate_password 플러그인 삭제 UNINSTALL PLUGIN validate_password; 복구할 경우 INSTALL PLUGIN validate_password SONAME 'validate_password.so'; 2. my.cnf에서 validate_password 플러그인 설정 조절 // my.cnf validate_password_policy=LOW // tty (ubuntu) sudo service mysql restart // tty (centos) sudo service mysqld restart

[MySQL] 유저 목록 확인하는 방법

USE mysql SELECT User, Host, Password FROM user; 참고: https://www.rosehosting.com/blog/mysql-show-users/