プログラムの事とか

お約束ですが「掲載内容は私個人の見解です」

WPFで雑にウォーターマーク付きのテキストボックスを作る

ウォーターマークのあるテキストボックス(未入力だと透かしがでるやつ)がたまにほしくなりますよね

ガッツリ作る必要もない時はテンプレートで適当に作ってしまえばいいんじゃない?

<Window.Resources>
    <Style x:Key="WatermarkTextbox" TargetType="{x:Type TextBox}">
        <Setter Property="Template">
            <Setter.Value>
                <ControlTemplate TargetType="{x:Type TextBox}">
                    <Grid Background="White">
                        <ScrollViewer x:Name="PART_ContentHost" Margin="5,0,0,0" VerticalAlignment="Center"/>
                        <TextBlock x:Name="WaterMarkLabel" Text="{TemplateBinding Tag}" Opacity=".5" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="5,0,0,0" Visibility="Collapsed"/>
                    </Grid>
                    <ControlTemplate.Triggers>
                        <Trigger Property="Text" Value="">
                            <Setter Property="Visibility" TargetName="WaterMarkLabel" Value="Visible"/>
                        </Trigger>
                    </ControlTemplate.Triggers>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>
</Window.Resources>
<Grid Background="Blue">
    <TextBox Style="{StaticResource WatermarkTextbox}" VerticalAlignment="Center" HorizontalAlignment="Center" Width="200" Tag="Input please"/>
</Grid>

できあがり

f:id:puni-o:20161215110421g:plain

TextBox.TagをTextBlock.Textに入れて、TriggerでTextが空白の時に表示しているだけです

Xamarin.iOSでUITextFieldの変更イベントをとる

CodeBehind(?)でUITextFieldの変更イベントを知りたいとおもいました

ほかのコントロール(UISlider等)と同じようにやればいいとおもっていました

var _text = new UITextField();

_text.ValueChanged += (sender,e)=>
{
// 変更
};

こんな感じに書いたんですが、これじゃダメだったんですね

_text.AddTarget((sender,e)=>
{
// 変更
},UIControlEvent.EditingChanged);

これなら呼ばれます

理由はNativeなやつにValueChangedが無いからとかなんとかってBugzillaに書いてありました

864 – ValueChanged event is not fired for UITextField

多分忘れたころにまた引っかかると思います

WPFのInkCanvasで塗りつぶす

f:id:puni-o:20161201111926j:plain

InkCanvasは線を描くものっぽくて閉じた領域の塗りつぶし、みたいなことは標準ではできなさそうでした

ということでとりあえず適当に塗りつぶすようにしていみます

塗りつぶす人

System.Windows.Ink.Strokeを継承してDrawCoreで塗りつぶすようにします

ソース

public class CustomInkCanvas : InkCanvas
{
protected override void OnStrokeCollected(InkCanvasStrokeCollectedEventArgs e)
{
    this.Strokes.Remove(e.Stroke);
    var newStroke = new FillStroke(e.Stroke.StylusPoints, e.Stroke.DrawingAttributes);
    this.Strokes.Add(newStroke);

    base.OnStrokeCollected(new InkCanvasStrokeCollectedEventArgs(newStroke));
}
}

public class FillStroke : Stroke
{
public FillStroke(StylusPointCollection stylusPoints) : base(stylusPoints)
{
}
public FillStroke(StylusPointCollection stylusPoints, DrawingAttributes drawingAttributes) : base(stylusPoints, drawingAttributes)
{
}

protected override void DrawCore(DrawingContext drawingContext, DrawingAttributes drawingAttributes)
{
    if (drawingContext == null)
    {
        throw new ArgumentNullException(nameof(drawingContext));
    }
    if (null == drawingAttributes)
    {
        throw new ArgumentNullException(nameof(drawingAttributes));
    }

    if (this.StylusPoints.Count < 3) return;

    var stroke = new Pen(Brushes.Blue, 2);
    stroke.Freeze();
    var fill = new SolidColorBrush(Color.FromArgb(0x80, 0x00, 0xff, 0xff));
    fill.Freeze();

    var streamGeometry = new StreamGeometry();
    using (var geometryContext = streamGeometry.Open())
    {
        geometryContext.BeginFigure(this.StylusPoints[0].ToPoint(), true, false);
        var points = new PointCollection();
        for (var i = 1; i < this.StylusPoints.Count; i++)
        {
            points.Add(this.StylusPoints[i].ToPoint());
        }
        geometryContext.PolyLineTo(points, true, false);
    }

    drawingContext.DrawGeometry(fill, stroke, streamGeometry);
}
}

CustomInkCanvasはStrokeが追加されたときにFillStrokeに置き換える処理をしています

FillStroke.DrawCoreで塗りつぶしています

結果

f:id:puni-o:20161201112430g:plain

塗りつぶせました

あとは閉じた領域だけ塗るようにすればいい感じになりそうなんですが、閉じた領域ってどうやって探すんだろう・・・