English | 中文
This project comes from some scattered works I did while working on Wpf development, and is a supplement to existing Mvvm frameworks. They don't solve any major problems, they just provide some syntactic sugar and let people write a few lines of code less. Its services are not limited to Wpf development, other similar Xaml frameworks, such as Uwp, Maui, etc. should also be able to use, but I has never tested on other frameworks.
The project is structured as follows:
WpfExtensions.Xaml
:A number ofMarkupExtension
s are provided to simplify Xaml development.WpfExtensions.Binding
:To simplify the code for property dependency updates, a function similar to the one in Vue.js for computed-property is provided.WpfExtensions.Infrastructure
:Some scattered features, when the time is ripe, will be separated out and released as separate modules.
Package | NuGet |
---|---|
WpfExtensions.Xaml |
|
WpfExtensions.Binding |
Brings some of the functionality of the Reactivity module from Vue3 into Wpf.
The term "observable" as used in the following documentation refers to objects that implement
INotifyPropertyChanged
orINotifyCollectionChanged
.
Subscribe to an observable expression and trigger a callback function when its value changes.
// See the source code for more overloads, whose signatures are consistent with vue3's `watch()`, and the vue3 documentation for examples.
Reactivity.Default.Watch(() => Width * Height, area => Debug.WriteLine(area));
Deep traversal subscribes to an observable object and triggers a callback function when its properties, or the properties of its properties, change.
// `path` will print out the path to the specific property that was changed.
Reactivity.Default.WatchDeep(obj, path => Debug.WriteLine(path))
Computed property that is an instance method of the BindableBase
base class.
public class ViewModel : BindableBase {
// Can be bound to xaml to automatically notify Area changes when Width or Height changes.
public double Area => Computed(() => Width * Height);
}
- View (XAML):
<Element Command={markup:Command Execute} />
<Element Command={markup:Command ExecuteWithArgumentAsync, CanExecute}
CommandParameter={Binding Argument} />
- View Model (*.cs):
class ViewModel
{
public void Execute() {}
public void ExecuteWithArgument(string arg) {}
// The `Execute` method supports async, and its default `Can Execute` method will disable the command when it is busy.
public Task ExecuteAsync() => Task.Completed;
public Task ExecuteWithArgumentAsync(string arg) => Task.Completed;
// The `Can Execute` method does not support async.
public bool CanExecute() => true;
public bool CanExecuteWithArgument(string arg) => true;
}
Combine multiple Converters into one pipeline.
<TextBlock Visibility="{Binding DescriptionText, Converter={markup:Compose
{StaticResource IsNullOrEmptyOperator},
{StaticResource NotConverter},
{StaticResource BooleanToVisibilityConverter}}}"
Text="{Binding DescriptionText}" />
Using the Conditional expression
in XAML.
<Button Command="{markup:If {Binding BoolProperty},
{Binding OkCommand},
{Binding CancelCommand}}" />
<UserControl>
<markup:If Condition="{Binding IsLoading}">
<markup:If.True>
<views:LoadingView />
</markup:If.True>
<markup:If.False>
<views:LoadedView />
</markup:If.False>
</markup:If>
</UserControl>
Using the Switch expression
in XAML.
<Image Source="{markup:Switch {Binding FileType},
{Case {x:Static res:FileType.Music}, {StaticResource MusicIcon}},
{Case {x:Static res:FileType.Video}, {StaticResource VideoIcon}},
{Case {x:Static res:FileType.Picture}, {StaticResource PictureIcon}},
...
{Case {StaticResource UnknownFileIcon}}}" />
<UserControl>
<Switch To="{Binding SelectedViewName}">
<Case Label="View1">
<views:View1 />
</Case>
<Case Label="{x:Static res:Views.View2}">
<views:View2 />
</Case>
<Case>
<views:View404 />
</Case>
</Switch>
</UserControl>
Dynamically switch the culture resource without restarting the app.
<TextBlock Text="{markup:I18n {x:Static languages:UiStrings.MainWindow_Title}}" />
<TextBlock Text="{markup:I18nString {x:Static languages:UiStrings.SayHello}, {Binding Username}}" />
<TextBlock Text="{markup:I18nString {x:Static languages:UiStrings.StringFormat},
{Binding Arg0},
{Binding Arg1},
...,
{Binding Arg15}}" />
<Button Style="{markup:Styles {StaticResource FlatButtonStyle},
{StaticResource AnimationStyle},
...}" />