プログラムの事とか

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

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

塗りつぶせました

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

WPFのItemsControl.ItemTemplateでリストの一つ前を参照する

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

WPFのItemsControlはすごく強力だとおもいますよね

ItemsControl.ItemsSourceにリストをバインドして、ItemTemplateで中身を定義すると自由なリストが簡単に作れるので一度使うと癖になります

簡単な例だとこんな感じ

<ItemsControl ItemsSource="{Binding Numbers}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="5">
                <TextBlock Text="{Binding}"/>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

Numbersはランダムな10個の数値リストです

実行結果はランダムな数値が縦に10個並ぶだけなので割愛

普段はこんな感じでお手軽に使えますがごくごくごく偶にアイテムの前の値が欲しい時があります(主にデータ設計に問題がある時ですかねぇ)

そんな時は

{Binding RelativeSource={RelativeSource PreviousData}}

で見ることができるんですね、今日知りました

先ほどのサンプルを更新すると

<ItemsControl Grid.Column="1" ItemsSource="{Binding Numbers}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" Margin="5">
                <TextBlock Text="{Binding RelativeSource={RelativeSource PreviousData}}"/>
                <TextBlock Text=" -> "/>
                <TextBlock Text="{Binding}"/>
            </StackPanel>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

前の値(リストの一つ上の項目)から今回の値に変わったよ、的な表示をしています

実行結果は

f:id:puni-o:20161115165310p:plain

左側は初めに書いたもにで、右が更新後です

こんなことも簡単にできるんですねー

WPFつよい