WPF Notes

Why is 'StringFormat' malfunctioning?

About Data Type

If you are binding a string value to text property of TextBlock or TextBox, then append StringFormat, it will work.
For example: <TextBlock Text="{Binding StringValue, StringFormat={} I am special: {0}}" />
Otherwise, let's say if there's a object value is bound to text, it won't work at all. In this situation, you need a Converter to solve.

Decimal Type

If you are binding a string value to text property and the StringFormat is like: {}{0:N0}(formatted number), it won't work either. This also won't work in c# code like string.Format("{0:n0}", value)(if value is astring)

Why does my UI text blurry after apply DropShadowEffect?

  • Solution 1: Separate Control which applied DropShadowEffect from your text Control.
  • Solution 2: Add RenderingBias="Quality" attribute to DropShadowEffect. See ref
  • Solution 3: DropShadowEffect may cause performance issue, try SystemDropShadowChrome instead:
    • Add reference to project: PresentationFramework.Aero
    • Add using: xmlns:dropShadow="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero"

How to debug codes in design mode?

Here are the steps to debug a control at design time:

Start a second instance of Visual Studio
Attach to the first instance(vs designer is called XDesProc.exe) from the Debug menu
In the second instance, open the source code of your control
Set breakpoints at appropriate locations
In the first VS instance, reload the designer

Note that this technique isn't limited to controls; you can also use it to debug VS extensions, or anything that executes within Visual Studio.

see ref

How to binding control events to commands

<i:Interaction.Triggers>
    <i:EventTrigger EventName="OnLoaded">
        <i:EventTrigger.Actions>
            <i:InvokeCommandAction Command="{Binding MyCommand}"></i:InvokeCommandAction>
        </i:EventTrigger.Actions>
    </i:EventTrigger>
</i:Interaction.Triggers>

How to use async command execute

private async void MyCommandExecute()
{
    IsBusy = true;
    var models = await Task.Run(() =>
    {
        return GetDataFromServer();
    });
    IsBusy = false;
}

or in a better way, encapsulate DoWork method in ViewModelBase:

ViewModelBase.cs
rprc IsBusy;
public async void DoProgressAsync<TResult>(Func<TResult> workFunc, Action<TResult> callbackAction)
{
    IsBusy = true;
    var result = await Task.Run(workFunc);
    callbackAction(result);
    IsBusy = false;
}

Events for Switch TextBlock to TextBox when mousedown

Here is fields and properties in the custom control we need: _isMouseDown; IsEditMode(dependency property)

public override void OnApplyTemplate()
{
    _textblock.PreviewMouseLeftButtonDown += delegate { _isMouseDown = true; };
    _textblockv.PreviewMouseLeftButtonUp += delegate {
        if(_isMouseDown) {
          IsEditMode = true;
          Keyboard.Focus(_textBox);
          _isMouseDown = false;
        }
    };
    _label.GotKeyboardFocus += delegate{
       //same as left button up
    };
    _textBox.LostKeyboardFocus += delegate {
        IsEditMode = false;
    };
}

Simple Loading Ellipse

<ControlTemplate x:Key="LoadingControlTemplate">
        <Ellipse StrokeThickness="2" Width="12" Height="12" RenderTransformOrigin="0.5 0.5" ToolTip="{TemplateBinding ToolTip}" Name="el">
            <Ellipse.Stroke>
                <RadialGradientBrush GradientOrigin="0.2 0" Center="0.5 0.9"  RadiusX="0.75" RadiusY="0.5">
                    <GradientStop Color="Transparent" Offset="1" />
                    <GradientStop Color="White" Offset="0.35" />
                </RadialGradientBrush>
            </Ellipse.Stroke>
            <Ellipse.RenderTransform>
                <RotateTransform Angle="0" />
            </Ellipse.RenderTransform>
        </Ellipse>
        <ControlTemplate.Triggers>
            <Trigger Property="Visibility" Value="Visible">
                <Trigger.EnterActions>
                    <BeginStoryboard Name="ani">
                        <Storyboard>
                            <DoubleAnimation Storyboard.TargetProperty="(Ellipse.RenderTransform).(RotateTransform.Angle)"
                                             Storyboard.TargetName="el"
                                             To="360" Duration="0:0:0.75" RepeatBehavior="Forever" />
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.EnterActions>
                <Trigger.ExitActions>
                    <RemoveStoryboard BeginStoryboardName="ani"/>
                </Trigger.ExitActions>
            </Trigger>
        </ControlTemplate.Triggers>
    </ControlTemplate>

Solid color style ProgressBar

for Indeterminate

<!--Please use Foreground as Indicator color-->
<ControlTemplate x:Key="IndeterminateProgressBar" TargetType="{x:Type ProgressBar}" >
    <Grid MinHeight="2" MinWidth="150" Name="TemplateRoot" SnapsToDevicePixels="True" >
        <Rectangle RadiusX="2" RadiusY="2" Fill="Transparent" />
        <Border CornerRadius="0" Margin="1">
            <Border.Background>
                <SolidColorBrush Color="Transparent"/>
            </Border.Background>
        </Border>
        <Border BorderThickness="0" BorderBrush="Transparent" Margin="1">
            <Border.Background>
                <SolidColorBrush Color="Transparent"/>
            </Border.Background>
        </Border>
        <Rectangle Name="PART_Track" Margin="1" />
        <Decorator Name="PART_Indicator" Margin="1" HorizontalAlignment="Left">
            <Grid Name="Foreground">
                <Rectangle Fill="Transparent" Name="Indicator" />
                <Grid Name="Animation" ClipToBounds="True">
                    <Border Name="PART_GlowRect" Width="80"  Margin="0,0,0,0" HorizontalAlignment="Left" Background="{TemplateBinding Foreground}"/>
                </Grid>
                <Grid Name="Overlay">
                </Grid>
            </Grid>
        </Decorator>
        <Border BorderThickness="0" CornerRadius="0" BorderBrush="Transparent" />
    </Grid>
</ControlTemplate>

for Determinate

<!--Please use Foreground as Indicator color, Background as TotalWidth background-->
<ControlTemplate x:Key="DeterminateProgressBar" TargetType="{x:Type ProgressBar}">
    <Grid MinHeight="2" MinWidth="150" Background="{TemplateBinding Background}" SnapsToDevicePixels="True">
        <Border x:Name="PART_Track" BorderThickness="0">
        </Border>
        <Border x:Name="PART_Indicator" BorderThickness="0" HorizontalAlignment="Left" Background="{TemplateBinding Foreground}" Margin="0">
        </Border>
    </Grid>
</ControlTemplate>

Usage

<ProgressBar Template="{StaticResource IndeterminateProgressBar}" IsIndeterminate="True" Foreground="LightBlue" 
              Visibility="{Binding IsRefreshing, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<ProgressBar Template="{StaticResource DeterminateProgressBar}" Minimum="0" Maximum="{Binding ItemCount}" Value="{Binding CurrentItemIndex}" Foreground="Red" Background="Green"
              Visibility="{Binding IsLoadingFinished, Converter={StaticResource BooleanToVisibilityConverter}}"/>

**for 30 seconds pending progress

A little trick using rectangle by scale animation rather than ProgressBar with Timer:

<Style x:Key="PendingProgressRect" TargetType="Rectangle">
        <Setter Property="Height" Value="1"/>
        <Setter Property="HorizontalAlignment" Value="Stretch"/>
        <Setter Property="VerticalAlignment" Value="Stretch"/>
        <Setter Property="RenderTransform">
            <Setter.Value>
                <ScaleTransform ScaleX="0"></ScaleTransform>
            </Setter.Value>
        </Setter>
        <Style.Triggers>
            <Trigger Property="Visibility" Value="Visible">
                <Trigger.EnterActions>
                    <BeginStoryboard Name="pendingProgressAni">
                        <Storyboard>
                            <DoubleAnimationUsingKeyFrames
                                Storyboard.TargetProperty="RenderTransform.ScaleX"
                                Duration="0:0:30" Timeline.DesiredFrameRate="1">
                                <DiscreteDoubleKeyFrame Value="0.1" KeyTime="0:0:3" />
                                <DiscreteDoubleKeyFrame Value="0.2" KeyTime="0:0:6" />
                                <DiscreteDoubleKeyFrame Value="0.3" KeyTime="0:0:9" />
                                <DiscreteDoubleKeyFrame Value="0.4" KeyTime="0:0:12" />
                                <DiscreteDoubleKeyFrame Value="0.5" KeyTime="0:0:15" />
                                <DiscreteDoubleKeyFrame Value="0.6" KeyTime="0:0:18" />
                                <DiscreteDoubleKeyFrame Value="0.7" KeyTime="0:0:21" />
                                <DiscreteDoubleKeyFrame Value="0.8" KeyTime="0:0:24" />
                                <DiscreteDoubleKeyFrame Value="0.9" KeyTime="0:0:27" />
                                <DiscreteDoubleKeyFrame Value="1.0" KeyTime="0:0:29.7" />
                            </DoubleAnimationUsingKeyFrames>
                        </Storyboard>
                    </BeginStoryboard>
                </Trigger.EnterActions>
                <Trigger.ExitActions>
                    <RemoveStoryboard BeginStoryboardName="pendingProgressAni"/>
                </Trigger.ExitActions>
            </Trigger>
        </Style.Triggers>
    </Style>

AddSorted to List/Collection

/// <summary>
/// Adds a new item with sorted by comparer in the collection.
/// </summary>
public static void AddSorted<T>(this IList<T> list, T item, IComparer<T> comparer = null)
{
    var comparerPara = comparer;

    if (comparerPara == null)
        comparerPara = Comparer<T>.Default;

    int i = 0;
    while (i < list.Count && comparerPara.Compare(list[i], item) < 0)
    {
        i++;
    }

    //you may want to invoke this on UI thread if context is at background.
    list.Insert(i, item);
}

Then any model which has IComparable<T> implemented will take effect in this extension method.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容