Skip to content
This repository has been archived by the owner on Aug 14, 2024. It is now read-only.

uWidgets Contribution Guide

Илья edited this page Jul 16, 2023 · 3 revisions

Introduction

uWidget is a continuation of the MontereyRainmeter project. It is a C# solution, built using .NET Core 6 and the WPF framework.

This guide will provide all the information you need to contribute your custom widget to our application.

Getting Started

  1. Start by cloning the 2.0-net branch of this repository
  2. Create a new project using the WPF User Control Library template
  3. Add Shared project as a dependency
  4. Change the project's output path to ..\..\uWidgets\bin\Debug\Widgets\

YourProject.csproj

    <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
        <OutputPath>..\..\uWidgets\bin\Debug\Widgets\</OutputPath>
    </PropertyGroup>

Solution Structure

uWidgets solution contains of two main projects:

  • uWidgets project is a WPF application. Consider it as an entry point for the whole application.
  • Shared project is a class library. It contains classes and interfaces that may be useful for building custom widgets.

Settings

There are two .json files the project uses to store settings:

  • appSettings.json contains application's settings, ex. current theme and language. You can access it by using IAppSettingsProvider.
  • layout.json contains an array, where each element represents widget's layout and settings. You can access your widget's settings by using IWidgetSettingsProvider.

Widget

Widget class is located in Shared project, represents a WPF window that will contain some content. It wraps around your UserControl and takes responsibility for things like:

  • Resizing
  • Moving
  • Theming
  • Context menus

Widget Development

Here's an example of widget project's structure:

Clock/
  Views/
    AnalogClock.xaml
    AnalogClock.xaml.cs
    DigitalClock.xaml
    DigitalClock.xaml.cs
  Models/
    ClockViewModel.cs
    ClockSettings.cs

Views

Your project can contain multiple view classes. Each of them must be inherited from the UserControl class.

Here's example of a view class:

DigitalClock.xaml

<UserControl ...>
    <!-- Your widget layout here -->
</UserControl>

DigitalClock.xaml.cs

public partial class DigitalClock : UserControl
{
    public DigitalClock()
    {
        InitializeComponent();
    }
}

Settings

Your project must contain exactly one Settings class, inherited from the WidgetSettings class.

Here's example of Settings class:

public class ClockSettings : WidgetSettings
{
    public bool ShowSeconds { get; set; }
    public bool ShowAmPm { get; set; }
}

These settings will be stored in layout.json class, and you will be able to access them using IWidgetSettingsProvider

ViewModel

Your project must contain exactly one ViewModel class, implementing INotifyPropertyChanged interface.

Data Binding

ViewModel class represents current state of the widgets. This class is also used in Data Binding.

That means, you can access any property you had described in that class, from your .xaml view, like this:

ClockViewModel.cs

public class ClockViewModel : INotifyPropertyChanged
{
    public DateTime Time { get; set; }
    public string TimeString => Time.ToString("hh:mm:ss");
}

DigitalClock.xaml

<UserControl d:DataContext="{d:DesignInstance Type=models:ClockViewModel}" ...>
    <TextBlock Text="{Binding TimeString}" />
</UserControl>

Using Settings

You can access application's and widget's settings using IAppSettingsProvider and IWidgetSettignsProvider, like this:

public class ClockViewModel : INotifyPropertyChanged
{
    public DateTime Time { get; set; }
    public string TimeString => Time.ToString(timeFormat);

    private string timeFormat; 

    public ClockViewModel(IAppSettingsProvider appSettingsProvider, IWidgetSettingsProvider widgetSettingsProvider)
    {
        var showSeconds = widgetSettingsProvider.ShowSeconds;
        timeFormat = showSeconds ? "hh:mm:ss" : "hh:mm";
    }
}

Both of these interfaces provide Updated events. Subscribe to them to update your content if these data were changed.

Updating View

To update your view you need to raise PropertyChanged event with a name of the property that was changed. There's also a quick way to do it:

    protected virtual void Update()
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(string.Empty));
    }

Passing string.Empty as property name will notify that all ViewModel properties were changed.

Testing

Add an array element into layout.json, like that:

  {
    "Type": "<Your project name>", 
    "Subtype": "<Your UserControl class name>",
    "Id": "<Unique GUID>",
    "X": <int>,
    "Y": <int>,
    "Columns": <int>,
    "Rows": <int>,
    <your other settings, described in Settings class>
  }

Now, when you start uWidgets project, you will see your widget and be able to interact with it

Submission Process

Create a Pull Request so we can merge your widget into our solution.

We don't have any specific guidelines for Pull Requests yet, but we encourage clear and concise descriptions of your widget.