louzi

louzi 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑

努力做个优秀的程序员,写得一手漂亮的代码~

个人动态

louzi 发布了文章 · 2月24日

WPF -- DataTemplate与ControlTemplate结合使用

如深入浅出WPF中的描述,DataTemplate为数据的外衣,ControlTemplate为控件的外衣。ControlTemplate控制控件的样式,DataTemplate控制数据显示的样式,DataTemplate是ControlTemplate的一部分。本文介绍DataTemplate与ControlTemplate结合使用的方法,其关键在于ContentPresenter,它是DataTemplate的树根,代表DataTemplate的实例。

场景

自定义Button,使其显示当前页与总页数,当页码变化时自动更新。

实现步骤
  1. 自定义Button.ControlTemplate;
  2. 自定义Button.ContentTemplate;
  3. 创建数据类;
  4. 创建ViewModel类;
  5. 绑定。
示例代码:
// xaml
<UserControl.Resources>
    <viewmodel:TextViewModel x:Key="TestViewModel"/>
</UserControl.Resources>

<Grid DataContext="{StaticResource TextViewModel}">
    <Button Width="120" Height="50" Content="{Binding PageInfo}">
        <Button.Template>
            <ControlTemplate TargetType="Button">
                <ContentPresenter/>
            </ControlTemplate>
        </Button.Template>
        <Button.ContentTemplate>
            <DataTemplate>
                <TextBlock Width="{TemplateBinding Width}" TextAlignment="Center"
                           FontSize="36" FontFamily="微软雅黑" Foreground="#ffffff">
                    <Run Text="{Binding CurrentPage}"/>
                    <Run Text="/"/>
                    <Run Text="{Binding TotalPages}"/>
                </TextBlock>
            </DataTemplate>
        </Button.ContentTemplate>
    </Button>
</Grid>

// 数据类
public class PageInfo : ViewModelBase
{
    public PageInfo(string currentPage, string totalPages)
    {
        this.CurrentPage = currentPage;
        this.TotalPages = totalPages;
    }

    public string CurrentPage
    {
        get { return currentPage; }
        set
        {
            currentPage = value;
            OnPropertyChanged("CurrentPage");
        }
    }

    public string TotalPages
    {
        get { return totalPages; }
        set
        {
            totalPages = value;
            OnPropertyChanged("TotalPages");
        }
    }

    private string currentPage;
    private string totalPages;
}

// viewmodel类
public class TestViewModel : ViewModelBase
{
    public TextViewModel()
    {
        PageInfo = new PageInfo("1", "1");
    }

    public PageInfo PageInfo
    {
        get { return pageInfo; }
        set { pageInfo = value; }
    }

    // 其它逻辑

    private PageInfo pageInfo;
}
查看原文

赞 0 收藏 0 评论 0

louzi 发布了文章 · 2月22日

.Net -- NLog日志框架配置与使用

NLog是适用于各种.NET平台(包括.NET标准)的灵活,免费的日志记录平台,NLog可将日志写入多个目标,比如Database、File、Console、Mail。下面介绍下NLog的基本使用方法。

使用步骤

添加引用

安装NLog Nuget package:Install-Package NLog.Config;

添加配置文件
  1. 在项目中添加一个配置文件(也可以直接将NLog的配置项目放入*.exe.config中),重命名为NLog.config;
  2. 更改如下两项属性内容:

    • 复制到输出目录 - 如果较新则复制
    • 生成操作 - 内容
  3. 添加配置内容,下面展示的是GitHub上的示例配置:
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

    <!-- 配置输出目标 -->
    <targets>
        <!-- 输出到文件 -->
        <target name="logfile" xsi:type="File" fileName="file.txt" />
        <!-- 输出到控制台 -->
        <target name="logconsole" xsi:type="Console" />
    </targets>

    <!-- 定义输出规则,可配置多个 -->
    <rules>
        <logger name="*" minlevel="Info" writeTo="logconsole" />
        <logger name="*" minlevel="Debug" writeTo="logfile" />
    </rules>
</nlog>
  1. 创建Logger类,获取NLog.Logger实例:
public class Log
{
    // ...
    private static readonly NLog.Logger Logger = 
        NLog.LogManager.GetCurrentClassLogger();
}

配置示例

如下配置为本人常用配置,可参考该配置及GitHub指导文档自定义配置:

注意:首次使用NLog时,可先将throwExceptions置为true,这样如果配置有问题没有成功打印日志,VS会抛出异常,方便定位原因。调试好后,再将该项置为false。

另外:示例中fileName(日志文件全路径名称)是通过后台代码配置的,这样可以动态设置日志的输出位置。

// NLog.config
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      throwExceptions="false" autoReload="true" async="true" encoding="UTF-8">

  <targets>
    <target name="logconsole" xsi:type="Console" 
            layout="${date:format=HH\:mm\:ss} | ${level:padding=-5} | ${message}"/>
    <target name="logfile" xsi:type="File" createDirs="true" keepFileOpen="true"
            fileName="${gdc:logDirectory:whenEmpty=${baseDir}}/logs/${shortdate}/Whiteboard.log"
            archiveFileName="${gdc:logDirectory:whenEmpty=${baseDir}/logs/${shortdate}}/Whiteboard_{##}.log"
            archiveAboveSize="102400" archiveNumbering="Sequence" maxArchiveDays="30"
            layout="${longdate} | ${level:uppercase=false:padding=-5} | ${message} ${onexception:${exception:format=tostring} ${newline} ${stacktrace} ${newline}"/>
  </targets>

  <rules>
    <logger name="*" minlevel="Debug" writeTo="logconsole"/>
    <logger name="*" minlevel="Debug" writeTo="logfile" />
  </rules>
</nlog>

// Log.cs 设置日志文件输出位置
string logDir = Path.Combine(Environment.GetFolderPath(
    Environment.SpecialFolder.LocalApplicationData),
    Process.GetCurrentProcess().ProcessName);
NLog.GlobalDiagnosticsContext.Set("logDirectory", logDir);
查看原文

赞 0 收藏 0 评论 0

louzi 发布了文章 · 2月21日

Prism -- 简介

Prism是一个开源框架,用于在WPF、Xamarin Forms、Uno/Win UI等应用中创建松耦合、可维护、可测试的XAML应用程序。Prism提供了一组设计模式的实现,这些设计模式有助于编写结构良好且可维护的XAML应用程序,包括MVVM,dependency injection,commands,EventAggregator等。

Prism最初属于微软,后来该团队成员离开微软独立起来,但微软官网仍保留着Prism的文档只是不再更新。原来的Prism框架比较臃肿,2020年发布了8.0版本,已进行了很多改善。微软官方Prism文档很长,而且内容较老,如果想学习Prism框架,建议直接去GitHub,可以结合Prism-DocumentationPrism-Samples-Wpf一起看。

另外,Prism的核心成员Brian Lagunas和Dan Siegel在YouTube/Twitch平台上发布了一些视频及直播,如Brian Lagunas在一年前进行了Prism.Outlook的直播开发,该系列视频共11集,每集约两小时,有条件的可以进行观看。

Brian Lagunas是Prism的作者,也是Microsoft MVP/Xamarin MVP/Microsoft P&P Champion,拥有多年开发经验。通过看视频既能学习Prism框架,又能学习作者的一些开发理念及技巧,同时还能练习下英语听力,一举三得。

Prism提供了Visual Studio的Prism Template Pack插件,使用该插件可以快速的创建Prism应用及Prism模块。

查看原文

赞 0 收藏 0 评论 0

louzi 发布了文章 · 2月20日

Prism.WPF -- Prism框架使用(下)

本文参考Prism官方示例

命令使用

Prism提供了两种命令:DelegateCommand和CompositeCommand。

DelegateCommand

DelegateCommand封装了两个委托:Execute和CanExecute,使用如下:

// view
<Button Command="{Binding ExecuteDelegateCommand}" Content="DelegateCommand"/>

// viewmodel
public DelegateCommand ExecuteDelegateCommand { get; private set; }

public MainWindowViewModel()
{
    ExecuteDelegateCommand = new DelegateCommand(Execute, CanExecute);
}

private void Execute()
{
    UpdateText = $"Updated: {DateTime.Now}";
}

private bool CanExecute()
{
    return IsEnabled;
}
CompositeCommand

CompositeCommand为复合命令,由多个子命令构成。当调用CompositeCommand时,将依次调用每个子命令。默认情况下,当所有子命令CanExecute均返回true时才会执行CompositeCommand。使用方法如下:

// Project.Core中定义接口及实现
public interface IApplicationCommands
{
    CompositeCommand SaveCommand { get; }
}

public class ApplicationCommands : IApplicationCommands
{
    private CompositeCommand _saveCommand = new CompositeCommand();
    public CompositeCommand SaveCommand
    {
        get { return _saveCommand; }
    }
}

// App.xaml.cs中注册单例对象
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterSingleton<
        IApplicationCommands, ApplicationCommands>();
}

// viewmodel中添加子命令
public TabViewModel(IApplicationCommands applicationCommands)
{
    _applicationCommands = applicationCommands;

    UpdateCommand = new DelegateCommand(Update).ObservesCanExecute(
        () => CanUpdate);

    _applicationCommands.SaveCommand.RegisterCommand(UpdateCommand);
}

// view中执行命令(需在对应的viewmodel的构造函数中传入IApplicationCommands实例)
<Button Content="Save" Command="{Binding ApplicationCommands.SaveCommand}"/>

EventAggregator

EventAggregator是一种事件机制,解决了松耦合模块间的通信问题。使用方法如下:

// Project.core中声明事件类型
public class MessageSentEvent : PubSubEvent<string>
{
}

// viewmodel中发布事件
IEventAggregator _ea;
public MessageViewModel(IEventAggregator ea)
{
    _ea = ea;
    // 发布事件的命令
    SendMessageCommand = new DelegateCommand(SendMessage);
}

private void SendMessage()
{
    _ea.GetEvent<MessageSentEvent>().Publish(Message);
}

// viewmodel中订阅事件
IEventAggregator _ea;
public MessageListViewModel(IEventAggregator ea)
{
    _ea = ea;
    _ea.GetEvent<MessageSentEvent>().Subscribe(MessageReceived);
    // 如下方式可以过滤事件,可通过第二个参数指定处理线程
    // _ea.GetEvent<MessageSentEvent>().Subscribe(MessageReceived, 
        ThreadOption.PublisherThread, false, 
        (filter) => filter.Contains("Brian"));
}

private void MessageReceived(string message)
{
    // hava a message
}

RegionNavigation

区别于View Discovery和View Injection,RegionNavigation可通过region名称与要导航的视图名称实现更通用的视图导航功能,使用如下:

// 模块类中注册导航视图
public void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterForNavigation<ViewA>();
}

// xaml导航命令
<Button Command="{Binding NavigateCommand}" CommandParameter="ViewA" >Navigate to View A</Button>

// viewmodel实现导航
public DelegateCommand<string> NavigateCommand { get; private set; }

public MainWindowViewModel(IRegionManager regionManager)
{
    _regionManager = regionManager;
    NavigateCommand = new DelegateCommand<string>(Navigate);
}

private void Navigate(string navigatePath)
{
    if (navigatePath != null)
        _regionManager.RequestNavigate("ContentRegion", 
            navigatePath, NavigationCompleted);
}

// 可指定导航完成回调
private void NavigationCompleted(NavigationResult result)
{
    // ...
}
INavigationAware接口

INavigationAware接口包含三个方法:OnNavigatedFrom、OnNavigatedTo、IsNavigationTarge。当ViewAViewModel及ViewBViewModel均实现了INavigationAware接口,ViewA导航到ViewB时,先调用ViewA的OnNavigatedFrom方法,然后调用ViewB的IsNavigationTarge,当其返回true时,调用OnNavigatedTo方法,若IsNavigationTarge返回false,创建新ViewB。示例如下:

public class ViewAViewModel : BindableBase, INavigationAware
{
    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        // ...
    }

    public bool IsNavigationTarget(NavigationContext navigationContext)
    {
        return true;
    }

    public void OnNavigatedFrom(NavigationContext navigationContext)
    {
        // ...
    }
}
IConfirmNavigationRequest接口

IConfirmNavigationRequest接口继承了INavigationAware接口,并添加了ConfirmNavigationRequest方法。若ViewAViewModel实现了IConfirmNavigationRequest接口,当ViewA导航到ViewB时,先调用ConfirmNavigationRequest方法,若continuationCallback()参数为true,将继续执行导航,执行OnNavigatedFrom方法;若continuationCallback()参数为false,停止导航。示例如下:

public class ViewAViewModel : BindableBase, IConfirmNavigationRequest
{
    public void ConfirmNavigationRequest(NavigationContext navigationContext, 
        Action<bool> continuationCallback)
    {
        bool result = true;

        if (MessageBox.Show("Do you to navigate?", "Navigate?", 
            MessageBoxButton.YesNo) == MessageBoxResult.No)
            result = false;

        continuationCallback(result);
    }

    public bool IsNavigationTarget(NavigationContext navigationContext)
    {
        return true;
    }

    public void OnNavigatedFrom(NavigationContext navigationContext)
    {  
    }

    public void OnNavigatedTo(NavigationContext navigationContext)
    {
    }
}
IRegionMemberLifetime接口

IRegionMemberLifetime接口只包含一个KeepAlive只读属性。其默认值为true,若其为false,则当该region导航到其它视图时,实现了该接口的当前视图将从IRegion.Views集合中移除并回收。若为true,即使导航到其它视图,该视图依然存在于IRegion.Views集合。示例如下:

public class ViewAViewModel : BindableBase, IRegionMemberLifetime
{
    public bool KeepAlive
    {
        get
        {
            return false;
        }
    }
}
参数传递

可使用NavigationParameters实现导航时的参数传递,使用方法如下:

// 导航命令
private void PersonSelected(Person person)
{
    var parameters = new NavigationParameters();
    parameters.Add("person", person);

    if (person != null)
        _regionManager.RequestNavigate("PersonDetailsRegion", 
            "PersonDetail", parameters);
}

// 参数处理
public void OnNavigatedTo(NavigationContext navigationContext)
{
    var person = navigationContext.Parameters["person"] as Person;
    // ...
}

public bool IsNavigationTarget(NavigationContext navigationContext)
{
    var person = navigationContext.Parameters["person"] as Person;
    // ...
}
Navigation Journal

Navigation Journal可以记录导航的过程,其通过IRegionNavigationJournal接口实现。通过Navigation Journal,可以实现向前/向后导航。示例如下:

// GoForward
public class PersonListViewModel : BindableBase, INavigationAware
{
    IRegionNavigationJournal _journal;
    public DelegateCommand GoForwardCommand { get; set; }

    public PersonListViewModel(IRegionManager regionManager)
    {
        ...
        GoForwardCommand = new DelegateCommand(GoForward, CanGoForward);
    }

    // IRegionNavigationJournal.GoBack到行至此
    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        _journal = navigationContext.NavigationService.Journal;
        GoForwardCommand.RaiseCanExecuteChanged();
    }

    private void GoForward()
    {
        _journal.GoForward();
    }

    private bool CanGoForward()
    {
        return _journal != null && _journal.CanGoForward;
    }
}

// GoBack
public class PersonDetailViewModel : BindableBase, INavigationAware
{
    IRegionNavigationJournal _journal;
    public DelegateCommand GoBackCommand { get; set; }

    public PersonDetailViewModel()
    {
        GoBackCommand = new DelegateCommand(GoBack);
    }

    public void OnNavigatedTo(NavigationContext navigationContext)
    {
        _journal = navigationContext.NavigationService.Journal;
    }

    private void GoBack()
    {
        _journal.GoBack();
    }
}

InvokeCommandAction

Prism提供了InvokeCommandAction以使ViewModel处理View的事件,示例如下:

// view xaml
<ListBox ItemsSource="{Binding Items}" SelectionMode="Single">
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="SelectionChanged">
            <prism:InvokeCommandAction Command="{Binding SelectedCommand}" 
                TriggerParameterPath="AddedItems" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
</ListBox>

// viewmodel
public DelegateCommand<object[]> SelectedCommand { get; private set; }

public MainWindowViewModel()
{
    ...
    SelectedCommand = new DelegateCommand<object[]>(OnItemSelected);
}

private void OnItemSelected(object[] selectedItems)
{
    if (selectedItems != null && selectedItems.Count() > 0)
        SelectedItemText = selectedItems.FirstOrDefault().ToString();
}
查看原文

赞 0 收藏 0 评论 0

louzi 发布了文章 · 2月19日

Prism.WPF -- Prism框架使用(上)

本文参考Prism官方示例

创建Prism项目

  1. 将App.xaml中的WPF标准Application替换为PrismApplication,移除StartupUri属性;
  2. 将App.xaml.cs中的基类改为PrismApplication;
  3. 必须实现PrismApplication中的两个抽象方法:RegisterTypes、CreateShell;
  4. RegisterTypes用来注册类型;
  5. CreateShell用来创建程序主窗口。

Region使用

  1. 在view xaml文件中使用prism:RegionManager.RegionName="SomeRegion"标记region;
  2. 创建自定义RegionAdapter类,继承自RegionAdapterBase<T>,override Adapt和CreateRegion方法;
  3. 在App.xaml.cs中通过override ConfigureRegionAdapterMappings方法注册自定义RegionAdapter,示例如下:
protected override void ConfigureRegionAdapterMappings(
    RegionAdapterMappings regionAdapterMappings)
{
    base.ConfigureRegionAdapterMappings(regionAdapterMappings);
    regionAdapterMappings.RegisterMapping(
        typeof(StackPanel), Container.Resolve<StackPanelRegionAdapter>());
}

View注入Region

有两种方法,第一种称为View Discovery,该方法适用于当region加载时就把视图注入到region场景;另外一种方法称为View Injection,该方法适用于当激发某一事件后view注入到region场景。

View Discovery

通过如下方法实现:

regionManager.RegisterViewWithRegion("ContentRegion", typeof(ViewA));
View Injection

通过如下方法实现,并可通过IRegion的Activate与Deactivate接口实现view的使能:

private void Button_Click(object sender, RoutedEventArgs e)
{
    var view = _container.Resolve<ViewA>();
    IRegion region = _regionManager.Regions["ContentRegion"];
    region.Add(view);
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    //activate view a
    _region.Activate(_viewA);
}

private void Button_Click_1(object sender, RoutedEventArgs e)
{
    //deactivate view a
    _region.Deactivate(_viewA);
}

添加视图模块

  1. 添加项目,在项目中添加继承自IModule的类,实现OnInitialized与RegisterTypes方法。一般在OnInitialized中添加View Discovery代码以将该模块的相关View注入到Region中;
  2. 在程序中添加模块。添加模块的方式很多,本文仅介绍使用代码的方式添加,方法如下:
// App.xaml.cs
protected override void ConfigureModuleCatalog(IModuleCatalog moduleCatalog)
{
    moduleCatalog.AddModule<ModuleA.ModuleAModule>();
}

匹配ViewModels

如果不修改命名规则,在xaml中为窗口/控件添加如下属性将自动匹配viewmodel:

prism:ViewModelLocator.AutoWireViewModel="True"

可以通过如下方法修改默认的viewmodel匹配规则,仍需在xaml中配置AutoWireViewModel:

// App.xaml.cs
protected override void ConfigureViewModelLocator()
{
    base.ConfigureViewModelLocator();

    ViewModelLocationProvider.SetDefaultViewTypeToViewModelTypeResolver(
        (viewType) =>
    {
        var viewName = viewType.FullName;
        var viewAssemblyName = viewType.GetTypeInfo().Assembly.FullName;
        var viewModelName = $"{viewName}ViewModel, {viewAssemblyName}";
        return Type.GetType(viewModelName);
    });
}

若不想修改匹配规则,且viewmodel名称不匹配默认规则,可通过如下方式匹配,仍需在xaml中配置AutoWireViewModel:

protected override void ConfigureViewModelLocator()
{
    base.ConfigureViewModelLocator();
    
    ViewModelLocationProvider.Register<MainWindow, CustomViewModel>();
}
查看原文

赞 0 收藏 0 评论 0

louzi 发布了文章 · 2月6日

Win32Api -- 关闭当前应用

本文介绍Windows系统下使用Win32API获取当前应用并关闭的方法。

思路
  1. 使用EnumWindows接口枚举当前窗口;
  2. 过滤掉不可用、隐藏、最小化的窗口;
  3. 过滤掉子窗口;
  4. 通过标题、类名过滤掉系统窗口;
  5. 使用PostMessage发送关闭窗口信息。
具体实现
// 过滤掉系统的一些窗口
private static string[] filterTitles = new string[1] { "program manager"};
private static string[] filterClasses = new string[5] { "shell_traywnd", "workerw", "button", "progman", "windows.ui.core.corewindow"};

private void CloseCurrentApp()
{
    CallBack sort = new CallBack(EnumCallback);
    EnumWindows(sort, 0);
    return;
}

private bool EnumCallback(IntPtr hwnd, int lParam)
{
    string title = GetWindowText(hwnd);
    StringBuilder className = new StringBuilder(256);
    int nRet = GetClassName(hwnd, className, className.Capacity);
    if (nRet == 0)
        className.Append("");

    if (!IsWindowVisible(hwnd))
        return true;

    if (!IsWindowEnabled(hwnd))
        return true;

    if (IsIconic(hwnd))
        return true;

    // 过滤掉子窗口
    IntPtr parent = GetParent(hwnd);
    string parentTitle = GetWindowText(parent);
    if (parent != IntPtr.Zero)
    {
        if (IsWindowVisible(parent) && IsWindowEnabled(parent))
            return true;
    }

    IntPtr owner = GetWindow(hwnd, GW_OWNER);
    if (owner != IntPtr.Zero)
    {
        if (IsWindowVisible(owner) && IsWindowEnabled(owner))
            return true;
    }

    if (!filterTitles.Contains(title.ToLower()) && !filterClasses.Contains(className.ToString().ToLower()))
    {
        PostMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
        Console.WriteLine("关闭窗口(句柄:{0}, 标题:{1})!", hwnd, title);

        #region 获取窗口信息
        int processID = -1;
        long threadID = -1;
        processID = GetWindowThreadProcessId(hwnd, out threadID);
        bool isiconic = IsIconic(hwnd);
        uint gwlStyle = (uint)GetWindowLong(hwnd, GWL_STYLE);

        IntPtr hProcess = OpenProcess(ProcessAccessFlags.QueryInformation, false, processID);
        string fullPath = "";
        if (hProcess != IntPtr.Zero)
        {
            int capacity = 1024;
            StringBuilder processName = new StringBuilder(capacity);
            QueryFullProcessImageName(hProcess, 0, processName, ref capacity);
            fullPath = processName.ToString(0, capacity);
            CloseHandle(hProcess);
        }

        Console.WriteLine("-------------------窗口info:---------------");
        Console.WriteLine("====标题:{0} 句柄:{1}====", title, hwnd);
        Console.WriteLine("====父窗口标题:{0} 父窗口句柄:{1}====", parentTitle, parent);
        Console.WriteLine("====进程ID:{0} 类名:{1}====", processID, className.ToString());
        Console.WriteLine("====进程名:{0}====", fullPath);
        Console.WriteLine("====isiconic:{0} 样式:{1}====", isiconic, gwlStyle);
        WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
        placement.length = System.Runtime.InteropServices.Marshal.SizeOf(placement);
        GetWindowPlacement(hwnd, ref placement);
        Console.WriteLine("====placement:{0}====", placement.showCmd);
        EnumPropsDelegate prop = new EnumPropsDelegate(EnumPropsProc);
        EnumProps(hwnd, prop);
        #endregion 获取窗口信息

        return false;
    }

    return true;
}

private bool EnumPropsProc(IntPtr hwnd, IntPtr lpszString, IntPtr hData)
{
    string propName = System.Runtime.InteropServices.Marshal.PtrToStringAnsi(lpszString);
    Console.WriteLine("====属性:{0} 数据:{1}====", propName, hData);
    return true;
}

#region Win32Api
public const int GWL_STYLE = (-16);
public const int GWL_EXSTYLE = (-20);
public const int GW_OWNER = 4;
public const int WS_EX_TOOLWINDOW = 0x00000080;
public const int WM_SYSCOMMAND = 0x0112;
public const int WM_CLOSE = 0x10;
public const int SC_CLOSE = 0xF060;

public delegate bool CallBack(IntPtr hwnd, int lparam);
public delegate bool EnumPropsDelegate(IntPtr hwnd, IntPtr lpszString, IntPtr hData);

[DllImport("user32.dll")]
public static extern int EnumWindows(CallBack x, int y);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int GetWindowText(IntPtr hWnd, System.Text.StringBuilder lpString, int nMaxCount);

[DllImport("user32.dll", CharSet = CharSet.Auto)]
public static extern int GetWindowTextLength(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int GetClassName(IntPtr hWnd, System.Text.StringBuilder lpClassName, int nMaxCount);

[DllImport("user32.dll")]
public static extern bool IsWindowVisible(IntPtr hwnd);

[DllImport("user32.dll")]
public static extern bool IsWindowEnabled(IntPtr hwnd);

[DllImport("user32.dll", EntryPoint = "IsIconic")]
public static extern bool IsIconic(IntPtr hWnd);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetParent(IntPtr hwnd);

[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindow(IntPtr hwndParent, int nCmd);

[DllImport("user32.dll", EntryPoint = "GetWindowLongA", SetLastError = true)]
public static extern long GetWindowLong(IntPtr hwnd, int nIndex);

[DllImport("user32.dll", EntryPoint = "PostMessageA", SetLastError = true)]
public static extern bool PostMessage(IntPtr hwnd, uint Msg, uint wParam, uint lParam);

[DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId", SetLastError = true,
     CharSet = CharSet.Unicode, ExactSpelling = true,
     CallingConvention = CallingConvention.StdCall)]
public static extern int GetWindowThreadProcessId(IntPtr hWnd, out long lpdwProcessId);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(
     ProcessAccessFlags processAccess,
     bool bInheritHandle,
     int processId
);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool QueryFullProcessImageName([In]IntPtr hProcess, [In]int dwFlags,
    [Out]System.Text.StringBuilder lpExeName, ref int lpdwSize);

[DllImport("coredll.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(IntPtr hObject);

[DllImport("user32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);

[DllImport("user32.dll")]
public static extern int EnumProps(IntPtr hWnd, EnumPropsDelegate lpEnumFunc);

public struct WINDOWPLACEMENT
{
    public int length;
    public int flags;
    public int showCmd;
    public System.Drawing.Point ptMinPosition;
    public System.Drawing.Point ptMaxPosition;
    public System.Drawing.Rectangle rcNormalPosition;
}

[Flags]
public enum ProcessAccessFlags : uint
{
    All = 0x001F0FFF,
    Terminate = 0x00000001,
    CreateThread = 0x00000002,
    VirtualMemoryOperation = 0x00000008,
    VirtualMemoryRead = 0x00000010,
    VirtualMemoryWrite = 0x00000020,
    DuplicateHandle = 0x00000040,
    CreateProcess = 0x000000080,
    SetQuota = 0x00000100,
    SetInformation = 0x00000200,
    QueryInformation = 0x00000400,
    QueryLimitedInformation = 0x00001000,
    Synchronize = 0x00100000
}

public static string GetWindowText(IntPtr hwnd)
{
    int capacity = GetWindowTextLength(hwnd) * 2;
    System.Text.StringBuilder lpString = new System.Text.StringBuilder(capacity);
    GetWindowText(hwnd, lpString, lpString.Capacity);
    if (lpString.Length > 0)
    {
        return lpString.ToString();
    }
    return string.Empty;
}
#endregion Win32Api
查看原文

赞 0 收藏 0 评论 0

louzi 发布了文章 · 2月6日

一种圆形识别方案

本文介绍一种圆形的识别方案。

识别流程
  1. 判断是否为封闭图形;
  2. 根据圆的方程,取输入点集中的1/6、3/6、5/6处的三个点,求得圆的方程,获取圆心及半径;
  3. 取点集中的部分点,计算点到圆心的距离与半径的比例,与设定的阈值比较,得出结果。~~~~
实现
public static bool IsCircle(List<Point> points, out Point center, out double radius)
{
    int len = points.Count;
    center = new Point();
    radius = 0;

    // 判断是否为封闭图形
    if (!IsClosedFigure(points))
        return false;

    int judgePointNum = len * 50 / 100;
    if (len < judgePointNum)
        return false;

    // 取链表上三个点作为判断圆的根据
    Point p1 = points[len / 6];
    Point p2 = points[len / 2];
    Point p3 = points[len * 5 / 6];
    if ((Math.Abs(p1.X - p2.X) < 100 && Math.Abs(p1.Y - p2.Y) < 100)
        || (Math.Abs(p1.X - p3.X) < 100 && Math.Abs(p1.Y - p3.Y) < 100)
        || (Math.Abs(p2.X - p3.X) < 100 && Math.Abs(p2.Y - p3.Y) < 100))
        return false;

    // 三个点确定圆的方程,获取圆心坐标及半径
    GetCircle(p1, p2, p3, out center, out radius);

    // 获取圆上平均分部的多个点,判断其到圆心的距离与半径之差是否在精度内
    for (int i = 0; i < judgePointNum; ++i)
    {
        // 获取圆上点
        Point p = points[len * i / judgePointNum];
        double deviation = Math.Abs(GetDistance(center, p) - radius);

        // 点在圆上的偏移量与半径的比值若大于固定值,则不为圆
        if (deviation/radius > MaxRatio)
            return false;
    }

    return true;
}
查看原文

赞 0 收藏 0 评论 0

louzi 发布了文章 · 1月30日

C#语言特性及发展史

本文按照C#语言的发展历史,介绍C#每个版本的新增特性,主要参考微软官方文档。了解这些语言特性可以帮助我们更高效的编写C#代码。

C# 1.0

与Visual Studio .NET 2002一起发布,该版本的C#非常像Java。

C# 1.2

随Visual Studio .NET 2003一起发布,主要是一些小改进。值得注意的是,从此版本开始,当IEnumerator实现IDisposable时,foreach循环中生成的代码会在IEnumerator上调用Dispose。

C# 2.0

与Visual Studio 2005一起发布。自2.0开始,C#打好了基础,开始追求解决一些严重影响开发者的难点。

  • 泛型:使用泛型优于创建派生自ArrayList的ListInt或强制转换方式
  • partial class
  • 匿名方法:delegate运算符创建一个可以转换为委托类型的匿名方法。从C# 3.0开始,可使用lambda表达式创建匿名方法。
  • 可空类型:可空类型T?表示其基础类型T的所有值及额外的null值。
  • 迭代器:允许使用foreach来检查List(或其它可枚举类型)中的所有项
  • 协变和逆变:实现数组类型、委托类型和泛型类型参数的隐式引用转换

C# 3.0

与Visual Studio 2008一起发布,但完整功能是在.NET Framework 3.5版本中发布的。此版本标志着C#发展过程中的重大更改。

C# 4.0

随Visual Studio 2010一起发布。

C# 5.0

随Visual Studio 2012一起发布,主要工作是适用于异步编程的async和await模型。

C# 6.0

随Visual Studio 2015一起发布。

C# 7.0

与Visual Studio 2017一起发布。

C# 7.1 7.2 7.3

此版本开始C#可以单点发行,编译器有-refout-refonly两个选项,可用于控制引用程序集生成。

C# 8.0

专门面向.NET Core的第一个主要C#版本。

C# 9.0

.NET 5.0支持C# 9.0

查看原文

赞 0 收藏 0 评论 0

louzi 发布了文章 · 1月19日

WPF -- 一种实现本地化的方法

本文介绍一种WPF程序实现本地化的方法。

步骤

首先,假设xaml文件中存在一个Button按钮,内容为“按钮”,实现本地化的步骤如下:

  1. 展开程序的Properties,双击Resources.resx文件进行编辑;
  2. 添加一条资源,名称为"btnContent",值为"按钮";
  3. 右键复制Resources.resx然后粘贴,改名为Resources.en-US.resx;
  4. 添加资源,名称为"btnContent",值为"button";
  5. 在需要使用该资源的xaml文件中,引入Properties名称空间:
xmlns:prop="clr-namespace:WpfApplication1.Properties"
  1. 使用方法:
<Button Content="{x:Static prop:Resources.btnContent}"/>
  1. 测试:
protected override void OnStartup(System.Windows.StartupEventArgs e)
{
    base.OnStartup(e);
    // 注释掉该行会显示汉语,否则会显示英语
    System.Threading.Thread.CurrentThread.CurrentUICulture = 
        new System.Globalization.CultureInfo("en-US");
}
查看原文

赞 0 收藏 0 评论 0

louzi 发布了文章 · 1月19日

WPF -- 一种添加静态资源的方式

本文介绍使用独立的xaml文件添加静态资源的方式。

步骤
  1. 创建XAML文件,如ImageButton.xaml,添加ResourceDictionary标签,并添加静态资源;
  2. 在App.xaml的Application.Resources标签中添加xaml资源文件;
  3. 在xaml界面文件中使用StaticResource使用静态资源。
示例
// ImageButton.xaml
<ResourceDictionary xmlns...>
    <Style x:Key="CustomImageButton" TargetType="Button">
        ...
    </Style>
</ResourceDictionary>

// App.xaml
<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="/WpfApplication1;component/CustomControls/ImageButton.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

// 使用ImageButton的xaml
<StackPanel>
    <Button Width="50" Height="30" Content="Button" FontSize="14" Style="{StaticResource CustomImageButton}" />
</StackPanel>
查看原文

赞 0 收藏 0 评论 0

认证与成就

  • 获得 0 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 2020-12-19
个人主页被 936 人浏览