Devexpress-WPF初體驗

iGotogo發表於2021-07-06

最近使用wpf devexpress做一個wpf小專案,中間遇到了一些問題,這裡記錄下,同時也跟大家分享分享

1、devexpress安裝
devexpress提供了很多控制元件,特別是各種形式的資料列表,做的又“花哨”,效能又好,很讓人羨慕。
我安裝的是20.1.3.0版本,安裝完成後,devexpress會在編譯器中新增專案模板和檔案模板(VS又慢了不少)、
生成Demo Center ,Demo Source Code,從建立專案到控制元件的使用和demo原始碼的檢視,一條龍服務。

一、devexpress基礎控制元件

devexpress提供了很多控制元件和例項專案,剛接觸的時候,讓人眼花繚亂啊。建議在使用前,把他們執行起來都看一遍,
這樣在使用的時候能大概知道使用哪種型別的控制元件更合適。

二、Devexpress MVVM

1、ViewModelBese:ViewModelBese繼承了BindableBase,BindableBase實現了INotifyPropertyChanged介面。

自定義繫結屬性最簡單的寫法:

public string UserName
{
    get { return GetValue<string>(); }
    set { SetValue(value); }
}

public ObservableCollection<ProductModel> ProductList
{
    get { return GetValue<ObservableCollection<ProductModel>>(); }
    set { SetValue(value); }
}

2、POCO ViewModel

已經有了個ViewModelBese為什麼還需要POCO ViewModel呢?
這裡的POCO是:Plain Old CLR Objects,官方給的解釋,主要作用是簡化你的ViewModel

POCO (Plain Old CLR Objects) View Models simplify and speed up the development process.

POCO View Models allow you to:

Define bindable properties as simple auto-implemented properties.
Create methods that function as commands at runtime.
Make properties and methods implement MVVM-specific interfaces.
This allows you to create clean, simple, maintainable, and testable MVVM code.

The POCO View Models are fully compatible with any WPF control.

You can use View Models Generated at Compile Time to generate boilerplate code for your ViewModels at compile time.

3、Messenger

跟MVVMlight的是Messenger使用差不多,但需要了解的是:

如果使用的是Messenger.Default.Register訂閱訊息,因為Messenger.Default是弱引用的信使,不會導致記憶體洩漏,因此不需要呼叫Messenger.Default.Unregister取消訂閱

4、命令Commands

Xaml:

<Button Command="{Binding Path=TestCommand}" />

ViewModel:

public void Test(){}

public bool CanTest(){}

5、非同步命令Asynchronous Commands

Xaml:

<dxlc:LayoutControl Orientation="Vertical" VerticalAlignment="Top">
<ProgressBar Minimum="0" Maximum="100" Value="{Binding Progress}" Height="20"/>
<Button Content="Calculate" Command="{Binding CalculateCommand}"/>
<Button Content="Cancel" Command="{Binding CalculateCommand.CancelCommand}"/>
</dxlc:LayoutControl>

ViewModel:

public AsyncCommand CalculateCommand { get; private set; }

async Task Calculate() 
{
    for(int i = 0; i <= 100; i++) 
    {
        if(CalculateCommand.IsCancellationRequested) 
        {
            Progress = 0;
            return;
        }
        Progress = i;
        await Task.Delay(20);
    }
}

int _Progress;
public int Progress
{
    get { return _Progress; }
    set { SetValue(ref _Progress, value); }
}

public AsyncDelegateCommandsViewModel() 
{
    CalculateCommand = new AsyncCommand(Calculate);
}

6、訊息框MessageBox

Xaml:

<dxmvvm:Interaction.Behaviors>
<dx:DXMessageBoxService x:Name="DXMessageBoxService" />
</dxmvvm:Interaction.Behaviors>

ViewModel:

IMessageBoxService MessageBoxService
{
    get { return ServiceContainer.GetService<IMessageBoxService>(); }
}

使用:

MessageBoxService.ShowMessage("xxx!", "提示", MessageButton.OK, MessageIcon.Information);

7、執行緒管理服務DispatcherService

Xaml:

<dxmvvm:Interaction.Behaviors>
<dxmvvm:DispatcherService />
</dxmvvm:Interaction.Behaviors>

ViewModel:

IDispatcherService DispatcherService
{
    get { return this.GetService<IDispatcherService>(); }
}

//非同步
DispatcherService.BeginInvoke( async ()=>
{
    await Task.Delay(1000);
    ...
});

//同步
DispatcherService.Invoke(new Action(()=> { ... }));

8、匯出服務Export

Service:

IExportService CustomerExportService
{
    get { return GetService<IExportService>(); }
}

public interface IExportService
{
    void ExportToXls(string fileName);
}

public class ExportService : ServiceBase, IExportService
{
    public TableView View
    {
        get { return (TableView)GetValue(ViewProperty); }
        set { SetValue(ViewProperty, value); }
    }
    
    public static readonly DependencyProperty ViewProperty = DependencyProperty.Register("View", typeof(TableView), typeof(ExportService), new PropertyMetadata(null));        
        
    public void ExportToXls(string fileName)
    {
        if (View == null) return;
        View.ExportToXls(fileName);
    }
}

Xaml:

<dxmvvm:Interaction.Behaviors>
    <services:ExportService View="{x:Reference View}" />
</dxmvvm:Interaction.Behaviors>

<dxg:GridControl x:Name="GridControl"
    dx:ScrollBarExtensions.ScrollBarMode="TouchOverlap"
    AllowColumnMRUFilterList="False"
    AutoExpandAllGroups="True"
    DockPanel.Dock="Left"
    ItemsSource="{Binding Path=Customers}"
    SelectedItem="{Binding Path=SelectedCoustomer}"
    ShowBorder="False">
<dxmvvm:Interaction.Behaviors>
    <dxmvvm:EventToCommand Command="{Binding UpdateCommand}"     EventName="MouseDoubleClick" />
    <dxmvvm:EventToCommand Command="{Binding SelectionChangedCommand}" EventName="SelectedItemChanged" />
</dxmvvm:Interaction.Behaviors>
<dxg:GridControl.GroupSummary>
    <dxg:GridSummaryItem SummaryType="Count" />
</dxg:GridControl.GroupSummary>
<dxg:GridControl.Columns>
    <dxg:GridColumn Width="1*"
        Binding="{Binding Path=AutoId}"
        FieldName="xxx" />
    <dxg:GridColumn Width="3*"
        Binding="{Binding Path=IssueTime}"
        FieldName="日期">
    <dxg:GridColumn.EditSettings>
        <dxe:DateEditSettings Mask="G" MaskUseAsDisplayFormat="True" />
    </dxg:GridColumn.EditSettings>
    </dxg:GridColumn>
</dxg:GridControl.Columns>
<dxg:GridControl.TotalSummary>
    <dxg:GridSummaryItem Alignment="Right" SummaryType="Count" />
    <dxg:GridSummaryItem FieldName="FullName" SummaryType="Count" />
</dxg:GridControl.TotalSummary>
<dxg:GridControl.View>
<dxg:TableView x:Name="View"
    AllowBestFit="True"
    AllowCascadeUpdate="True"
    AllowDragDrop="False"
    AllowFixedGroups="True"
    AllowPrintColumnHeaderImage="True"
    AllowScrollAnimation="True"
    BestFitMode="Smart"
    HighlightItemOnHover="True"
    NavigationStyle="Row"
    ShowFixedTotalSummary="True"
    UseLegacyFilterPanel="False" />
    </dxg:GridControl.View>
</dxg:GridControl>

viewmodel:

CustomerExportService.ExportToXls(@"Files\customer.xls");

9、載入控制元件LoadingDecorator

Xaml:

<dx:LoadingDecorator IsSplashScreenShown="{Binding Path=IsLoading}"
OwnerLock="Full"
SplashScreenLocation="CenterWindow">
...
</dx:LoadingDecorator>

ViewModel:

public virtual bool IsLoading
{
    get { return GetValue<bool>(nameof(IsLoading)); }
    set { SetValue(value, nameof(IsLoading)); }
}

9、IOC容器Castle.Windsor

//整合Castle.Windsor
public class IocManager
{
    private static IWindsorContainer _Instance;
    private static readonly object syncRoot = new object();
    
    private IocManager()
    {
    }
    
    public static IWindsorContainer Instance
    {
        get
        {
            lock (syncRoot)
            {
                if (_Instance == null)
                {
                _Instance = new WindsorContainer().Install(FromAssembly.This());    
                }
                return _Instance;
            }
        }
    }
}

//註冊(具體需要註冊哪些例項,根據需要來定,這裡舉個例子)
IocManager.Instance.Register(
//view
Component.For<MainWindow>().LifestyleSingleton(),

//viewmodel
Component.For<MainViewModel>().LifestyleSingleton(),

//service
Component.For<ILoginService, LoginService>().LifestyleTransient());

//使用
MainWindow = IocManager.Instance.Resolve<MainWindow>();
MainWindow.ShowDialog();

10、整合log4net

首先需要配置好log4net,然後再結合Castle.Windsor的Logging Facility,將log4net注入到Castle.Windsor的容器中

注入:

IocManager.Instance.AddFacility<LoggingFacility>(p => p.LogUsing<Log4netFactory>().WithConfig("log4net.config"));

使用:

private ILogger logger = NullLogger.Instance;

public ILogger Logger
{
    get { return logger; }
    set { logger = value; }
}

Logger.Info("Castle.Windsor Logging Facilityd的使用");

總的來說Devexpress設計的還是很友好的,使用起來比較順暢。