« 2013年6月30日 - 2013年7月6日 | トップページ | 2014年5月11日 - 2014年5月17日 »

2013.07.11

[WinRT]ListViewの先頭位置

Windows APIのListBoxだと、LB_GETTOPINDEXで取ってこれる。MFCだとCListBox.GetTopIndex()になる。さて、WinRTの人だとどうやら直接は無理らしい。ListViewが持っているBorderが持っているScrollViewerをとってこないといけないっぽい。

ここで、Windows APIのGetWindow系の走査は、VisualTreeHelper.GetChild/GetChildrenCountでできるっぽい。

var border = VisualTreeHelper.GetChild(listView,0);
ScrollViewer scrollViewer = VisualTreeHelper.GetChild(border,0) as ScrollViewer;

順番変わったら、どうするの?とか、書きかけとかあるからdoubleね。とか、いろいろある。でも、 一番驚いたのが、2.0ベースらしいってこと。つまり、一番先頭が2.0で始まる。っぽい。少なくともそうなった。

ということで、ListView.Loadedのタイミングで、上記のようなソースで、scrollViewerを見つけつつ、VerticalOffsetを覚えておく必要がある。さらに、VieweChanged経由でVerticalOffsetの変更が検知できるっぽいので、こんな感じにしてみた。

            ListView.Loaded += (sender, e) =>
            {
                var border = VisualTreeHelper.GetChild(ListView, 0);
                ScrollViewer _scrollViewer = VisualTreeHelper.GetChild(border, 0) as ScrollViewer;
                double _verticalOffsetZero = _scrollViewer.VerticalOffset;
                _scrollViewer.ViewChanged += (sender2, e2) =>
                {
                    double topIndex = _scrollViewer.VerticalOffset - _verticalOffsetZero;	//正しい先頭位置(たぶん)
                    double count = _scrollViewer.ViewportHeight;	//個数
                };
            };

| | コメント (0) | トラックバック (0)

2013.07.10

[WinRT]続・非同期と排他

ちょこちょことasync/await周りを眺めてみた。やはりロックは別途必要な模様。

ロックがないままのasync/awaitって利用価値ってどのくらい?
そもそも、async/awaitはどうだろう。ちょこちょこっとしたのが非同期になっちゃっているんでやむなくのものかなという気もしつつ。便利は便利かも。
でも、WinRTからC#に入ってくると、ロックの使い方がよくわからない。
・lockステートメント:簡単だけど、awaitが中では使えない。
・Monitor.Enter/Exit:lockステートメントの実体。
書くことはできるけど使えない。
Monitor.Enter(object);
Debug.WriteLine("ENTER");
doSomething();
Debug.WriteLine("EXIT");
Monitor.Exit(object);

としても、ガンガンENTERが被る。たぶん、全部UIスレッド呼び出しだから、再入可能で入ってくるのかなと。

・Mutex・Semaphore・SemaphoreSlim:どれでも使えそう。でも、C#なら、SemaphoreSlimがいいっぽい。
MSDN:Semaphore と SemaphoreSlim参照。 あと、usingを使うと、勝手に解放されてくれるらしいので、その仕組みを使って、ざっくりでっち上げた。

public class AsyncLock
{
    private SemaphoreSlim _semaphore;
    public AsyncLock()
    {
        _semaphore = new SemaphoreSlim(1);
    }
    public AsyncLock(int initialCount)
    {
        _semaphore = new SemaphoreSlim(initialCount);
    }
    public AsyncLock(int initialCount, int maxCount)
    {
        _semaphore = new SemaphoreSlim(initialCount, maxCount);
    }
    public class Locker : IDisposable
    {
        private SemaphoreSlim _semaphore;
        internal Locker(SemaphoreSlim semaphore)
        {
            this._semaphore = semaphore;
        }
        public void Dispose()
        {
            _semaphore.Release();
        }
    }
    public async Task

LockAsync()     {         await _semaphore.WaitAsync();         return new Locker(_semaphore);     } }

無名クラスも作れない模様なので、usingのために、IDisaposableを返すようにして、派生クラスをLockerとして公開したけど、名前は適当。Lockにしてみたけど、Lock lockってのは書けないので、まあいいやと。

AsyncLock lock = new AsyncLock();
async void Foo()
{
    using(AsyncLock.Locker locker = await asyncLock.LockAsync()){
        doSomething();
    }
}

という感じで使う。AsyncLock.Lockerにしているけど、当然IDisposableでも大丈夫。なんとなく雰囲気で。でも、Async並びすぎ。 さて、IDisposableから、delegateを受け付けるようにして、それをラムダ式で渡せるクラスがあれば、いいけど、探すのが面倒なので、この辺にて。

| | コメント (0) | トラックバック (0)

2013.07.09

[WinRT]非同期と排他

よくわからない......async/awaitでなんとなく排他制御はうまくいくようになっている。

async Task Func1Async() {
    Func2();
    await Func3Async();
    Func4();
}

とあったとき、ちゃんとFunc1Async()->Func2()->Func3Async()->Func4()の順番で同じスレッドで呼ばれる。
でも、Func4()は、Func3Async()の後処理として呼ばれる。 これは、Func1Async()の呼び出し側でもawaitしないとダメであることを意味する。

GoodFunction()
{
    await Func1Async();
    Func1After();
}
BadFunction()
{
    Func1Async();
    Func1After();
}

このBadFunction()の例では、Func1Async()->Func2()->Func3Async()で別スレッド処理を呼び出して、その後Func1After()が呼ばれる。その後Func3After()のスレッドが終了後後処理としてFunc4()が呼ばれる。
awaitを使うと関数内の非同期呼び出しをしらっと同期関数っぽく書くことはできるけど、その書いた関数が同期関数になるわけではない。
理解してみると、当然だけど、最初はいまいち納得しなかった.......

さて、そのようにして調子づいて書いていたんだけど、やっぱり排他が必要な目にあった......

ReadDataAsync()
{
   if(completed)  return;①
   using(TextReader reader = await GetReaderAsync()){②
      doSomething();③
      complted = true;④
   }
}

この③・④の処理は、②の後処理スレッドとして実行される。別の呼び出しの、①の判定処理のが先に行われる場合がある。とりあえず、今回は、③の前に、判定をもう一個入れたんだけど、おおむね大丈夫だけど、やっぱり駄目かもしれない。さらに、ReadAsync()とか使うと、まるでうまくいかない.......どうすればいいんだろう.......lock使うと怒られるし......AsyncLockなるソースが落ちていたのでそれを使うのがいいのかな。でも、標準でもっとうまくできるよね......そういうもんなのかな........とはまり中。

そもそも、こんな事態になっていることが間違い???

| | コメント (0) | トラックバック (0)

2013.07.07

[WinRT]LastModifiedをとる。

今までは、Socketを使って自前でやってきたけど、今回はSystem.Net.Http.HttpClientを使っている。

HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, url);
HttpResponseMessage response = await httpClient.SendAsync(request,HttpCompletionOption.ResponseHeadersRead);
using (Stream readStream = await response.Content.ReadAsStreamAsync())
using (Stream writeStream = await UK2chUtil.GetStreamForWriteAsync(filename))
{
    await readStream.CopyToAsync(writeStream);
}

こんな感じでやっていて、response.Headersにあるかと思って、調べまくったけど、全然入ってこない.......サーバーからやってきていないかと、あきらめようとしたけど、ふと、MSDNでHttpResourceMessage.Headersの型が、HttpResponseHeadersでその上がHttpHeadersそれを見ると派生クラスが3人いる。

System.Net.Http.Headers::HttpHeaders
    System.Net.Http.Headers::HttpContentHeaders
    System.Net.Http.Headers::HttpRequestHeaders
    System.Net.Http.Headers::HttpResponseHeaders

HttpResponseHeaders・HttpRequestHeadersは応答と要求で残りのHttpContentHeadersはコンテンツ?でresponseからは直接は見れない......さらにさまよって以下のようにとれるようでした。

response.Content.Headers.LastModified

便利なんだろうけど、なかなか情報にたどり着けないので、だんだんとつまらなくなってくる.....

| | コメント (0) | トラックバック (0)

« 2013年6月30日 - 2013年7月6日 | トップページ | 2014年5月11日 - 2014年5月17日 »