728x90
반응형
728x170
▶ ControlExtension.cs
using System;
using System.Windows.Controls;
using System.Windows.Threading;
namespace TestProject
{
/// <summary>
/// 컨트롤 확장
/// </summary>
public static class ControlExtension
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Static
//////////////////////////////////////////////////////////////////////////////// Public
#region 필요시 UI 스레드에서 호출하기 - InvokeOnUIThreadIfRequired(control, action)
/// <summary>
/// 필요시 UI 스레드에서 호출하기
/// </summary>
/// <param name="control">컨트롤</param>
/// <param name="action">액션</param>
public static void InvokeOnUIThreadIfRequired(this Control control, Action action)
{
if(!control.Dispatcher.CheckAccess())
{
control.Dispatcher.BeginInvoke
(
DispatcherPriority.Normal,
new Action(delegate () { action(); })
);
}
else
{
action.Invoke();
}
}
#endregion
}
}
728x90
▶ AudioControl.xaml
<UserControl x:Class="TestProject.AudioControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontFamily="나눔고딕코딩"
FontSize="16">
<UserControl.Resources>
<Style x:Key="SliderRepeatButtonStyleKey" TargetType="{x:Type RepeatButton}">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Focusable" Value="false" />
<Setter Property="IsTabStop" Value="false" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Rectangle Fill="Transparent" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="HorizontalSliderThumbStyleKey" TargetType="{x:Type Thumb}">
<Setter Property="Width" Value="16" />
<Setter Property="Height" Value="16" />
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="Focusable" Value="false" />
<Setter Property="Foreground" Value="Gray" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Thumb}">
<Ellipse
StrokeThickness="0.8"
Stroke="#ff5e5d5d"
Fill="#ffaed1ed" />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type Slider}">
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="Foreground" Value="#ffc4c4c4" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Slider}">
<Border
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true">
<Grid>
<Grid.RowDefinitions>
<RowDefinition MinHeight="{TemplateBinding MinHeight}" Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Border Grid.Row="1"
VerticalAlignment="Center"
Margin="5 0"
Height="10"
CornerRadius="1">
<Grid>
<Border
CornerRadius="0.5"
Background="#fff7f4f4"
Opacity="0.215" />
<Border
Margin="0 0 0.3 0.3"
CornerRadius="0.5"
Background="#ff132e50"
Opacity="0.715" />
</Grid>
</Border>
<Track Name="PART_Track" Grid.Row="1">
<Track.DecreaseRepeatButton>
<RepeatButton
Style="{StaticResource SliderRepeatButtonStyleKey}"
Command="{x:Static Slider.DecreaseLarge}" />
</Track.DecreaseRepeatButton>
<Track.IncreaseRepeatButton>
<RepeatButton
Style="{StaticResource SliderRepeatButtonStyleKey}"
Command="{x:Static Slider.IncreaseLarge}" />
</Track.IncreaseRepeatButton>
<Track.Thumb>
<Thumb Style="{StaticResource HorizontalSliderThumbStyleKey}" />
</Track.Thumb>
</Track>
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" Value="0.5" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="CustomProgressBarBackgroundSolidColorBrushKey"
Color="#55ffffff" />
<Style x:Key="ProgressBarStyleKey" TargetType="{x:Type ProgressBar}">
<Setter Property="Height" Value="5" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Background" Value="{StaticResource CustomProgressBarBackgroundSolidColorBrushKey}" />
<Setter Property="Minimum" Value="0" />
<Setter Property="Maximum" Value="100" />
</Style>
</UserControl.Resources>
<Border
BorderThickness="1"
BorderBrush="Black"
Padding="1">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<ComboBox Name="microphoneComboBox" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="3"
Width="250"
Height="25" />
<Button Name="openSoundControlButton" Grid.Row="0" Grid.RowSpan="2" Grid.Column="3"
Margin="5 0 0 0"
Width="70"
Content="제어판" />
<Button Name="muteMicrophoneButton" Grid.Row="1" Grid.Column="0"
Margin="0 5 0 5"
Width="20"
Height="20"
Content=""
Background="#ff4fed82" />
<Slider Name="volumeSlider" Grid.Row="1" Grid.Column="1"
VerticalAlignment="Center"
Width="90"
Maximum="100"
TickFrequency="10"
SmallChange="1"
Value="-1" />
<ProgressBar Name="volumeProgressBar" Grid.Row="1" Grid.Column="2"
Style="{DynamicResource ProgressBarStyleKey}"
VerticalAlignment="Center"
Width="80"
Height="20"
Background="#55050000"
IsIndeterminate="False"
Value="50" />
</Grid>
</Border>
</UserControl>
반응형
▶ AudioControl.xaml.cs
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using AudioSwitcher.AudioApi;
using AudioSwitcher.AudioApi.CoreAudio;
using AudioSwitcher.AudioApi.Observables;
using NAudio.CoreAudioApi;
using NAudio.Wave;
namespace TestProject
{
/// <summary>
/// 오디오 컨트롤
/// </summary>
public partial class AudioControl : UserControl
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Field
////////////////////////////////////////////////////////////////////////////////////////// Private
#region Field
/// <summary>
/// 코어 오디오 장치 리스트
/// </summary>
private List<CoreAudioDevice> coreAudioDeviceList = null;
/// <summary>
/// 디폴트 코어 오디오 장치
/// </summary>
private CoreAudioDevice defaultCoreAudioDevice = null;
/// <summary>
/// 코어 오디오 컨트롤러
/// </summary>
private CoreAudioController coreAudioController = null;
/// <summary>
/// Disposable 리스트
/// </summary>
private List<IDisposable> disposableList = null;
/// <summary>
/// 볼륨 변경 액션
/// </summary>
private Action<DeviceVolumeChangedArgs> changeVolumeAction = null;
/// <summary>
/// 음소거 변경 액션
/// </summary>
private Action<DeviceMuteChangedArgs> changeMuteMicrophoneAction = null;
/// <summary>
/// 디폴트 장치 변경 액션
/// </summary>
private Action<DefaultDeviceChangedArgs> changeDefaultDeviceAction = null;
/// <summary>
/// 장치 상태 변경 액션
/// </summary>
private Action<DeviceStateChangedArgs> changeDeviceStateAction = null;
/// <summary>
/// 멀티미디어 장치 열거자
/// </summary>
private MMDeviceEnumerator mmDeviceEnumerator = null;
/// <summary>
/// 웨이브 입력 이벤트
/// </summary>
private WaveInEvent waveInEvent = null;
/// <summary>
/// 중단 여부
/// </summary>
private bool stopped = false;
/// <summary>
/// 최대 오디오 값
/// </summary>
private double maximumAudioValue = 0;
/// <summary>
/// 마지막 오디오 값
/// </summary>
private double lastAudioValue = 0;
/// <summary>
/// 샘플 비율
/// </summary>
private const int SAMPLE_RATE = 44100;
/// <summary>
/// 버퍼 샘플 수
/// </summary>
private const int BUFFER_SAMPLE_COUNT = 1024;
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - AudioControl()
/// <summary>
/// 생성자
/// </summary>
public AudioControl()
{
InitializeComponent();
Loaded += UserControl_Loaded;
Unloaded += UserControl_Unloaded;
this.microphoneComboBox.SelectionChanged += microphoneComboBox_SelectionChanged;
this.openSoundControlButton.Click += openSoundControlButton_Click;
this.muteMicrophoneButton.Click += muteMicrophoneButton_Click;
this.volumeSlider.ValueChanged += volumeSlider_ValueChanged;
}
#endregion
//////////////////////////////////////////////////////////////////////////////////////////////////// Method
////////////////////////////////////////////////////////////////////////////////////////// Private
//////////////////////////////////////////////////////////////////////////////// Event
#region 사용자 컨트롤 로드시 처리하기 - UserControl_Loaded(sender, e)
/// <summary>
/// 사용자 컨트롤 로드시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
this.microphoneComboBox.Cursor = Cursors.Hand;
this.muteMicrophoneButton.Cursor = Cursors.Hand;
this.volumeSlider.Cursor = Cursors.Hand;
this.volumeSlider.Maximum = 100;
this.volumeSlider.Value = 0;
this.volumeProgressBar.Value = 0;
this.openSoundControlButton.Cursor = Cursors.Hand;
this.changeVolumeAction = ChangeVolume;
this.changeMuteMicrophoneAction = ChangeMuteMicrophone;
this.changeDefaultDeviceAction = ChangeDefaultDevice;
this.changeDeviceStateAction = ChangeDeviceState;
this.disposableList = new List<IDisposable>();
this.mmDeviceEnumerator = new MMDeviceEnumerator();
this.coreAudioController = new CoreAudioController();
SetCoreAudioDeviceList();
SetControl();
}
#endregion
#region 사용자 컨트롤 언로드시 처리하기 - UserControl_Unloaded(sender, e)
/// <summary>
/// 사용자 컨트롤 언로드시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
if(this.defaultCoreAudioDevice != null)
{
this.defaultCoreAudioDevice.Dispose();
}
if(this.coreAudioController != null)
{
this.coreAudioController.Dispose();
}
if(this.mmDeviceEnumerator != null)
{
this.mmDeviceEnumerator.Dispose();
}
if(this.waveInEvent != null)
{
this.waveInEvent.Dispose();
}
}
#endregion
#region 마이크로폰 콤보 박스 선택 변경시 처리하기 - microphoneComboBox_SelectionChanged(sender, e)
/// <summary>
/// 마이크로폰 콤보 박스 선택 변경시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void microphoneComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(this.microphoneComboBox.SelectedIndex > -1)
{
if(!this.defaultCoreAudioDevice.FullName.Equals(this.microphoneComboBox.SelectedItem.ToString()))
{
CoreAudioDevice coreAudioDevice = this.coreAudioDeviceList.Find(x => x.FullName.Equals(this.microphoneComboBox.SelectedItem.ToString()));
if(coreAudioDevice != null)
{
this.coreAudioController.SetDefaultDevice(coreAudioDevice);
}
}
}
}
#endregion
#region 사운드 컨트롤 열기 버튼 클릭시 처리하기 - openSoundControlButton_Click(sender, e)
/// <summary>
/// 사운드 컨트롤 열기 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void openSoundControlButton_Click(object sender, RoutedEventArgs e)
{
// 제어판/소리/녹음 탭을 연다(마지막 0은 재생 탭, 1은 녹음 탭)
Process.Start("rundll32", "Shell32.dll,Control_RunDLL Mmsys.cpl,,1");
}
#endregion
#region 마이크로폰 음소거 버튼 클릭시 처리하기 - muteMicrophoneButton_Click(sender, e)
/// <summary>
/// 마이크로폰 음소거 버튼 클릭시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void muteMicrophoneButton_Click(object sender, RoutedEventArgs e)
{
if(this.defaultCoreAudioDevice != null)
{
this.defaultCoreAudioDevice.Mute(!this.defaultCoreAudioDevice.IsMuted);
}
}
#endregion
#region 볼륨 슬라이더 값 변경시 처리하기 - volumeSlider_ValueChanged(sender, e)
/// <summary>
/// 볼륨 슬라이더 값 변경시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void volumeSlider_ValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
{
if(this.defaultCoreAudioDevice != null)
{
this.defaultCoreAudioDevice.Volume = (int)e.NewValue;
}
}
#endregion
#region 웨이브 입력 이벤트 데이터 이용 가능시 처리하기 - waveInEvent_DataAvailable(sender, e)
/// <summary>
/// 웨이브 입력 이벤트 데이터 이용 가능시 처리하기
/// </summary>
/// <param name="sender">이벤트 발생자</param>
/// <param name="e">이벤트 인자</param>
private void waveInEvent_DataAvailable(object sender, WaveInEventArgs e)
{
float maximum = 0;
for(int i = 0; i < e.BytesRecorded; i += 2)
{
short sample = (short)((e.Buffer[i + 1] << 8) | e.Buffer[i + 0]);
float sampleFloat = sample / 32768f;
if(sampleFloat < 0)
{
sampleFloat = -sampleFloat;
}
if(sampleFloat > maximum)
{
maximum = sampleFloat;
}
}
if(maximum > this.maximumAudioValue)
{
this.maximumAudioValue = (double)maximum;
}
this.lastAudioValue = maximum;
double fraction = this.lastAudioValue / this.maximumAudioValue;
this.InvokeOnUIThreadIfRequired(() => this.volumeProgressBar.Value = (int)(fraction * this.volumeProgressBar.Width));
}
#endregion
//////////////////////////////////////////////////////////////////////////////// Function
#region Disposable 리스트 지우기 - ClearDisposableList()
/// <summary>
/// Disposable 리스트 지우기
/// </summary>
private void ClearDisposableList()
{
if(this.disposableList != null)
{
if(this.disposableList.Count > 0)
{
foreach(IDisposable disposable in this.disposableList)
{
disposable.Dispose();
}
}
this.disposableList.Clear();
}
}
#endregion
#region 코어 오디어 장치 리스트 설정하기 - SetCoreAudioDeviceList()
/// <summary>
/// 코어 오디어 장치 리스트 설정하기
/// </summary>
private void SetCoreAudioDeviceList()
{
if(this.coreAudioDeviceList == null)
{
this.coreAudioDeviceList = new List<CoreAudioDevice>();
}
else
{
this.coreAudioDeviceList.Clear();
}
IEnumerable<CoreAudioDevice> deviceEnumerable = this.coreAudioController.GetCaptureDevices().Where
(
device => !device.FullName.Contains("Unknown") && device.State.Equals(AudioSwitcher.AudioApi.DeviceState.Active)
);
foreach(CoreAudioDevice coreAudioDevice in deviceEnumerable)
{
this.coreAudioDeviceList.Add(coreAudioDevice);
}
if(this.coreAudioDeviceList.Count > 0)
{
ClearDisposableList();
this.defaultCoreAudioDevice = this.coreAudioController.DefaultCaptureDevice;
for(int i = 0; i < this.coreAudioDeviceList.Count; i++)
{
CoreAudioDevice device = this.coreAudioDeviceList[i];
this.disposableList.Add(ObservableExtensions.Subscribe(device.VolumeChanged , changeVolumeAction ));
this.disposableList.Add(ObservableExtensions.Subscribe(device.MuteChanged , changeMuteMicrophoneAction));
this.disposableList.Add(ObservableExtensions.Subscribe(device.DefaultChanged, changeDefaultDeviceAction ));
this.disposableList.Add(ObservableExtensions.Subscribe(device.StateChanged , changeDeviceStateAction ));
}
}
}
#endregion
#region 장치 인덱스 구하기 - GetDeviceIndex()
/// <summary>
/// 장치 인덱스 구하기
/// </summary>
/// <returns>장치 인덱스</returns>
private int? GetDeviceIndex()
{
MMDevice device = this.mmDeviceEnumerator.GetDefaultAudioEndpoint
(
DataFlow.Capture,
NAudio.CoreAudioApi.Role.Multimedia
);
int? deviceIndex = 0;
if(device != null)
{
for(int i = 0; i < WaveIn.DeviceCount; i++)
{
if(device.FriendlyName.Equals(WaveIn.GetCapabilities(i).ProductName))
{
deviceIndex = i;
break;
}
}
}
else
{
return null;
}
return deviceIndex;
}
#endregion
#region 웨이브 입력 이벤트 설정하기 - SetWaveInEvent(waveInEvent)
/// <summary>
/// 웨이브 입력 이벤트 설정하기
/// </summary>
/// <param name="waveInEvent">웨이브 입력 이벤트</param>
private void SetWaveInEvent(ref WaveInEvent waveInEvent)
{
int? deviceIndex = GetDeviceIndex();
if(deviceIndex != null)
{
waveInEvent = new WaveInEvent();
waveInEvent.DeviceNumber = (int)deviceIndex;
waveInEvent.WaveFormat = new WaveFormat(44100, 16, 1);
waveInEvent.BufferMilliseconds = (int)((double)BUFFER_SAMPLE_COUNT / (double)SAMPLE_RATE * 1000.0);
waveInEvent.DataAvailable += waveInEvent_DataAvailable;
waveInEvent.RecordingStopped += (sender, e) => this.stopped = false;
this.InvokeOnUIThreadIfRequired(() => this.volumeProgressBar.Value = 0);
}
}
#endregion
#region 컨트롤 설정하기 - SetControl()
/// <summary>
/// 컨트롤 설정하기
/// </summary>
private void SetControl()
{
try
{
lock(this.microphoneComboBox)
{
this.InvokeOnUIThreadIfRequired(() => this.microphoneComboBox.Items.Clear());
if(this.coreAudioDeviceList != null && this.coreAudioDeviceList.Count > 0 && this.defaultCoreAudioDevice != null)
{
Action<int> action = (count) =>
{
for(int i = 0; i < count; i++)
{
this.microphoneComboBox.Items.Add(this.coreAudioDeviceList[i].FullName);
}
this.microphoneComboBox.SelectedIndex = this.coreAudioDeviceList.IndexOf(this.defaultCoreAudioDevice);
};
this.InvokeOnUIThreadIfRequired(() => action(this.coreAudioDeviceList.Count));
if(this.waveInEvent == null)
{
SetWaveInEvent(ref this.waveInEvent);
}
if((int)this.defaultCoreAudioDevice.Volume > -1)
{
this.InvokeOnUIThreadIfRequired(() => this.volumeSlider.Value = (int)this.defaultCoreAudioDevice.Volume);
}
if(this.defaultCoreAudioDevice.IsMuted)
{
this.InvokeOnUIThreadIfRequired(() => this.muteMicrophoneButton.Background = (SolidColorBrush)new BrushConverter().ConvertFromString("red"));
if(this.waveInEvent != null)
{
this.waveInEvent.StopRecording();
this.stopped = false;
this.InvokeOnUIThreadIfRequired(() => this.volumeProgressBar.Value = 0);
}
}
else
{
this.InvokeOnUIThreadIfRequired(() => this.muteMicrophoneButton.Background = (SolidColorBrush)new BrushConverter().ConvertFromString("green"));
if(this.waveInEvent != null && !this.stopped)
{
this.waveInEvent.StartRecording();
this.stopped = true;
}
}
}
else
{
this.InvokeOnUIThreadIfRequired(() => this.microphoneComboBox.SelectedIndex = -1);
}
}
}
catch(Exception exception)
{
MessageBox.Show(exception.ToString());
}
}
#endregion
#region 볼륨 변경하기 - ChangeVolume(e)
/// <summary>
/// 볼륨 변경하기
/// </summary>
/// <param name="e">이벤트 인자</param>
private void ChangeVolume(DeviceVolumeChangedArgs e)
{
if(e.Device.FullName.Equals(this.defaultCoreAudioDevice.FullName))
{
this.InvokeOnUIThreadIfRequired(() => this.volumeSlider.Value = (int)e.Volume);
}
}
#endregion
#region 마이크로폼 음소거 변경하기 - ChangeMuteMicrophone(e)
/// <summary>
/// 마이크로폼 음소거 변경하기
/// </summary>
/// <param name="e">이벤트 인자</param>
private void ChangeMuteMicrophone(DeviceMuteChangedArgs e)
{
if(e.Device.FullName.Equals(this.defaultCoreAudioDevice.FullName))
{
SetCoreAudioDeviceList();
SetControl();
}
}
#endregion
#region 디폴트 장치 변경하기 - ChangeDefaultDevice(e)
/// <summary>
/// 디폴트 장치 변경하기
/// </summary>
/// <param name="e">이벤트 인자</param>
private void ChangeDefaultDevice(DefaultDeviceChangedArgs e)
{
if(((CoreAudioDevice)e.Device).IsDefaultDevice)
{
if(this.waveInEvent != null)
{
this.waveInEvent.StopRecording();
this.waveInEvent.DataAvailable -= waveInEvent_DataAvailable;
this.waveInEvent = null;
}
SetCoreAudioDeviceList();
SetControl();
}
}
#endregion
#region 장치 상태 변경하기 - ChangeDeviceState(e)
/// <summary>
/// 장치 상태 변경하기
/// </summary>
/// <param name="e">이벤트 인자</param>
private void ChangeDeviceState(DeviceStateChangedArgs e)
{
this.InvokeOnUIThreadIfRequired(() => this.volumeProgressBar.Value = 0);
if(((CoreAudioDevice)e.Device).IsDefaultDevice)
{
if(this.waveInEvent != null)
{
this.waveInEvent.StopRecording();
this.waveInEvent.DataAvailable -= waveInEvent_DataAvailable;
this.waveInEvent = null;
}
}
SetCoreAudioDeviceList();
SetControl();
}
#endregion
}
}
300x250
▶ MainWindow.xaml
<Window x:Class="TestProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestProject"
Width="800"
Height="600"
Title="마이크로폰 볼륨 설정하기"
FontFamily="나눔고딕코딩"
FontSize="16">
<Grid>
<local:AudioControl />
</Grid>
</Window>
▶ MainWindow.xaml.cs
using System.Windows;
namespace TestProject
{
/// <summary>
/// 메인 윈도우
/// </summary>
public partial class MainWindow : Window
{
//////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
////////////////////////////////////////////////////////////////////////////////////////// Public
#region 생성자 - MainWindow()
/// <summary>
/// 생성자
/// </summary>
public MainWindow()
{
InitializeComponent();
}
#endregion
}
}
728x90
반응형
그리드형(광고전용)
'C# > WPF' 카테고리의 다른 글
[C#/WPF] ResourceDictionary 클래스 : 컴파일 및 동적 스키닝 사용하기 (기능 개선) (0) | 2022.01.09 |
---|---|
[C#/WPF] ResourceDictionary 클래스 : 컴파일 및 동적 스키닝 사용하기 (0) | 2022.01.09 |
[C#/WPF] ResourceDictionary 클래스 : 컴파일 및 정적 스키닝 사용하기 (0) | 2022.01.09 |
[C#/WPF] DoubleAnimation 클래스 : 화면을 반짝이는 애니메이션 사용하기 (0) | 2022.01.09 |
[C#/WPF] TextBox 클래스 : KeyDown 이벤트를 사용해 ENTER 키를 누르는 경우 포커스 해제하기 (0) | 2022.01.08 |
[C#/WPF] 정렬과 필터링 가능한 데이터 가상화 사용하기 (0) | 2022.01.06 |
[C#/WPF] Page 엘리먼트 : WindowWidth/WindowHeight/WindowTitle 속성 사용하기 (0) | 2022.01.04 |
[C#/WPF] Application 클래스 : GetRemoteStream 정적 메소드를 사용해 원본 사이트 파일 XAML 페이지 로드하기 (0) | 2022.01.02 |
[C#/WPF] 원본 사이트 파일 사용하기 (0) | 2022.01.02 |
[C#/WPF] Frame 엘리먼트 : Source 속성을 사용해 컨텐트 XAML 페이지 로드하기 (0) | 2022.01.02 |
댓글을 달아 주세요