2012年3月16日 星期五

[Rx]Reactive Observable方法的比較

網路上有一些範例,有些可以執行,有些連成員的方法都找不到,大部分都是因為使用了不同的Assembly,

所以下面列出這些不一樣的地方,我們就可以少走一些彎路啦。

  System.Reactive.Linq Microsoft.Phone.Reactive

簽名、種類都相同的

(黃底表示下面有類似的)

Aggregate
All(TSource)
Amb
And(TLeft, TRight)
Any
AsObservable(TSource)
Average
Cast(TResult)
Catch
CombineLatest(TLeft, TRight, TResult)
Concat
Contains
Count(TSource)
Defer(TValue)
Delay
Dematerialize(TSource)
DistinctUntilChanged
Do
Empty
Finally(TSource)
FromAsyncPattern
FromEvent
Generate
GetEnumerator(TSource)
GroupBy
Interval
Latest(TSource)
LongCount(TSource)
Materialize(TSource)
Max
MaxBy
Merge
Min
MinBy
MostRecent(TSource)
Never(TValue)
Next(TSource)
ObserveOn
OfType(TResult)
OnErrorResumeNext
Publish
Range
RefCount(TSource)
Repeat
Replay
Retry
Return
Sample
Scan
Select
SelectMany
Skip(TSource)
SkipLast(TSource)
SkipUntil(TSource, TOther)
Start
StartWith
Subscribe
SubscribeOn
Sum
Switch(TSource)
Synchronize
TakeLast(TSource)
TakeUntil(TSource, TOther)
Then(TSource, TResult)
Throttle
Throw
TimeInterval
Timeout
Timer
Timestamp
ToAsync
ToEnumerable(TSource)
ToObservable
Using(TSource, TResource)
Where
Zip

有部分不相同者

(黃底表示上面有類似的)

 

Buffer
Create
First
FirstOrDefault
Join(TLeft, TRight, TLeftDuration, TRightDuration, TResult)
Last
LastOrDefault
Single
SingleOrDefault
SkipWhile
Take(TSource)
TakeWhile
BufferWithCount
BufferWithTime
BufferWithTimeOrCount
Create(TSource)
CreateWithDisposable(TSource)
First(TSource)
FirstOrDefault(TSource)
Join
Last(TSource)
LastOrDefault(TSource)
Single(TSource)
SingleOrDefault(TSource)
SkipWhile(TSource)
Take
TakeWhile(TSource)
 

只有一邊有的


(黃底表示上面有類似的)

DefaultIfEmpty
Distinct
ElementAt(TSource)
ElementAtOrDefault(TSource)
ForEach(TSource)
FromEventPattern
GroupByUntil
GroupJoin(TLeft, TRight, TLeftDuration, TRightDuration, TResult)
IgnoreElements(TSource)
Multicast
PublishLast
SequenceEqual
ToArray(TSource)
ToDictionary
ToEvent
ToEventPattern(TEventArgs)
ToList(TSource)
ToLookup
When
Window
Case
DoWhile(TSource)
For(TSource, TResult)
ForkJoin
GenerateWithTime
If(TResult)
IsEmpty(TSource)
Let
ObserveOnDispatcher(TSource)
Prune
RemoveTimeInterval(TSource)
RemoveTimestamp(TSource)
Run
Scan0(TSource, TAccumulate)
SubscribeOnDispatcher(TSource)
While(TSource)

參考資料:

2012年3月14日 星期三

[Rx]無法將 Lambda 運算式 轉換為型別 'System.IObserver<string>',因為它不是委派型別

 

image

結果是:需要 using Microsoft.Phone.Reactive; 完全和轉型沒有關係,哈哈。

另一種情況是Lambda大括號裡面的敘述有錯誤造成的。

這是我看過最奇怪的一個錯誤訊息了。

2012年3月12日 星期一

[WP7]由檔案路徑讀取IsolatedStarage的圖檔

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Windows.Data;
using System.Windows.Media.Imaging;
using Microsoft.Phone;
namespace XXXX.Common
{
    public class FilePathToImageConverter : IValueConverter
    {
        private static IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
        public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            string path = value as string;
            if (String.IsNullOrEmpty(path))
                return null;
            if ((path.Length > 9) && (path.ToLower().Substring(0, 9).Equals("isostore:")))
            {
                using (IsolatedStorageFileStream isfs = isf.OpenFile(path.Substring(9), FileMode.Open, FileAccess.Read))
                {
                    WriteableBitmap wb = PictureDecoder.DecodeJpeg(isfs, 80, 50);
                    return wb;                    
                }
            }
            else
            {
                BitmapImage bi = new BitmapImage(new Uri(path, UriKind.Relative));
                return bi;
            }
        }
        public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}

可以這樣使用

<common:FilePathToImageConverter x:Key="FilePathToImageConverter" />
<Image x:Name="Image1" Source="{Binding ImageSource,Converter={StaticResource FilePathToImageConverter}}" Width="80" Height="50" ></Image>

2012年2月19日 星期日

WPF體系的邏輯樹/視覺樹常用操作

 

適用WPF,部分適用於Silverlight。

using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Media;

namespace OrcaPlayer.Common
{
public class TreeHelper
{
private static object _SyncRoot = new object();

<summary>
IsolatedStorageHelper Static Constructor.
</
summary>
static TreeHelper()
{

}

private static TreeHelper _instance = null;
<summary>
</
summary>
public static TreeHelper Instance
{
get
{
lock (_SyncRoot)
{
if (_instance == null)
{
_instance = new TreeHelper();
}
return _instance;
}
}
}


public DependencyObject FindVirtualTreeRoot(DependencyObject initial)
{
DependencyObject current = initial;
DependencyObject result = initial;

while (current != null)
{
result = current;

if (current is Visual || current is Visual3D)
{
current = VisualTreeHelper.GetParent(current);
}
else
{
current = LogicalTreeHelper.GetParent(current);
}
}

return result;
}

public DependencyObject FindLogicalTreeRoot(DependencyObject initial)
{
DependencyObject current = initial;
DependencyObject result = initial;

while (current != null)
{
result = current;
current = LogicalTreeHelper.GetParent(current);
}

return result;
}

//using System.Windows;
public DependencyObject GetTemplateParent(DependencyObject depObj)
{
FrameworkElement fe = depObj as FrameworkElement;
FrameworkContentElement fce = depObj as FrameworkContentElement;

DependencyObject result;

if (fe != null)
{
result = fe.TemplatedParent;
}
else if (fce != null)
{
result = fce.TemplatedParent;
}
else
{
result = null;
}

return result;
}

//最近邏輯樹上的父節點
public DependencyObject FindClosestLogicalAncestor(DependencyObject initial)
{
DependencyObject current = initial;
DependencyObject result = initial;

while (current != null)
{
DependencyObject logicalParent = LogicalTreeHelper.GetParent(current);

if (logicalParent != null)
{
result = current;
break;
}

current = VisualTreeHelper.GetParent(current);
}

return result;
}

//最近視覺樹上的父節點
public DependencyObject FindClosestVisualAncestor(DependencyObject initial)
{
if (initial is Visual || initial is Visual3D)
{
return initial;
}

DependencyObject current = initial;
DependencyObject result = initial;

while (current != null)
{
result = current;

if (current is Visual || current is Visual3D)
{
result = current;
break;
}
else
{
current = LogicalTreeHelper.GetParent(current);
}
}

return result;
}

//顯示邏輯樹
//private TreeView tree = new TreeView();
public void PopulateLogicalTree(TreeViewItem parentItem, object currentElement)
{
TreeViewItem item = new TreeViewItem();

item.IsExpanded = true;

if (parentItem == null)
{
item.Header = currentElement.GetType().Name;
tree.Items.Add(item);
}
else
{
item.Header = currentElement.GetType().Name;
parentItem.Items.Add(item);
}

DependencyObject curObject = currentElement as DependencyObject;

if (curObject != null)
{
foreach (object chElement in LogicalTreeHelper.GetChildren(curObject))
{
PopulateLogicalTree(item, chElement);
}
}
}

//顯示視覺樹
//private TreeView tree = new TreeView();
public void PopulateVisualTree(TreeViewItem parentItem, DependencyObject currentElement)
{
TreeViewItem item = new TreeViewItem();

item.IsExpanded = true;

if (parentItem == null)
{
item.Header = currentElement.GetType().Name;
tree.Items.Add(item);
}
else
{
item.Header = currentElement.GetType().Name;
parentItem.Items.Add(item);
}

int visualChildrenCount = VisualTreeHelper.GetChildrenCount(currentElement);

for (int i = 0; i < visualChildrenCount; i++)
{
DependencyObject visualChild = VisualTreeHelper.GetChild(currentElement, i);
PopulateVisualTree(item, visualChild);
}
}

//ex: CheckBox chb = this.FindFirstVisualChild<CheckBox>(listViewItem)
public T FindFirstVisualChild<T>(DependencyObject parentElement) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parentElement); i++)
{
DependencyObject child = VisualTreeHelper.GetChild(parentElement, i);

if (child != null && child is T)
{
return child as T;
}
else
{
T result = FindFirstVisualChild<T>(child);

if (result != null)
{
return result;
}
}
}

return null;
}

//TextBlock textBlock = GetFrameworkElementByName<TextBlock>(rowPresenter, "textBlock");
public T GetFrameworkElementByName<T>(FrameworkElement parentElement, String name) where T : FrameworkElement
{
FrameworkElement child = null;

for (Int32 i = 0; i < VisualTreeHelper.GetChildrenCount(parentElement); i++)
{
Debug.WriteLine("TreeHelper.cs::GetFrameworkElementByName " + parentElement.GetType().ToString() + " " + i.ToString());
child = VisualTreeHelper.GetChild(parentElement, i) as FrameworkElement;

if (child != null && child.Name == name && child.GetType() == typeof(T))
{
break;
}

else if (child != null)
{
child = GetFrameworkElementByName<T>(child, name);
}
}

return child as T;
}

//ContentPresenter cp = VisualTreeHelper.GetChild(CInfoContentControl, 0) as ContentPresenter;
//Button DownloadButton = TreeHelper.Instance.FindVisualChild<Button>(cp, 0, 3, 0, 0, 1) as Button;
//第二參數為第一參數下的的每一層子元素(子元素從0起算)
public T FindVisualChild<T>(DependencyObject parentElement, params int[] list) where T : DependencyObject
{
DependencyObject child = null;
for (int i = 0; i < list.Length; i++)
{
Debug.WriteLine("i=[" + i.ToString() + "] list[i]=[" + list[i].ToString() + "]");

if (i == 0)
{
if (VisualTreeHelper.GetChildrenCount(parentElement) > 0)
{
child = VisualTreeHelper.GetChild(parentElement, list[0]);
Debug.WriteLine("子元素型別=[" + child.GetType().ToString() + "]");
}
else
{
Debug.WriteLine(i.ToString() + " 子元素個數=[" + VisualTreeHelper.GetChildrenCount(parentElement).ToString() + "]");
}
}
else
{
if (VisualTreeHelper.GetChildrenCount(child) > 0)
{
child = VisualTreeHelper.GetChild(child, list[i]);
Debug.WriteLine("子元素型別=[" + child.GetType().ToString() + "]");
}
else
{
Debug.WriteLine(i.ToString() + " 子元素個數=[" + VisualTreeHelper.GetChildrenCount(child).ToString() + "]");
}
}
}

if (child != null && child is T)
{
return child as T;
}
else
{
return null;
}
}


public static T GetValue<T>(this BindingExpression expression, object dataItem)
{
if (expression == null || dataItem == null)
{
return default(T);
}

string bindingPath = expression.ParentBinding.Path.Path;
string[] properties = bindingPath.Split('.');

object currentObject = dataItem;
Type currentType = null;

for (int i = 0; i < properties.Length; i++)
{
currentType = currentObject.GetType();
PropertyInfo property = currentType.GetProperty(properties[i]);
if (property == null)
{
currentObject = null;
break;
}
currentObject = property.GetValue(currentObject, null);
if (currentObject == null)
{
break;
}
}

return (T)currentObject;
}

}
}


參考資料:


2012年1月26日 星期四

在啟動Expression Blend 4 時Crash

這是.Net 4.5 Developer Preview的相容性問題。這個問題已經被微軟修正了,但是修正的時間太晚了,所以沒有被放在下載的檔案中(preview build)。

修正方式為執行以下命令:

  • %windir%\Microsoft.NET\Framework\v4.0.30319\ngen uninstall "%ProgramFiles(x86)%\Microsoft Expression\Blend 4\Microsoft.Expression.Framework.dll"
  • %windir%\Microsoft.NET\Framework\v4.0.30319\ngen uninstall "%ProgramFiles(x86)%\Microsoft Expression\Blend 4\Microsoft.Expression.Blend.dll"
  • %windir%\Microsoft.NET\Framework\v4.0.30319\ngen uninstall "%ProgramFiles(x86)%\Microsoft Expression\Blend 4\Microsoft.Expression.Project.dll"
  • 如果有安裝Blend 4 SP1或後續的SPx,需要執行                                                                                                                                       %windir%\Microsoft.NET\Framework\v4.0.30319\ngen uninstall "%ProgramFiles(x86)%\Microsoft Expression\Blend 4\Microsoft.Expression.WindowsPhone.dll"

如果是使用x86系統的話,需要移除路徑中的"(x86)"。

參考資料:

2011年9月20日 星期二

馬上可用的WCF診斷設定

 

  1. 以下設定的initializeData目錄需有網站權限。
  2. switch這邊設定為All,代表會輸出所有診斷的訊息,可以依據需要來調整。
<system.diagnostics>
    <sources>
      <source name="System.ServiceModel.MessageLogging" switchValue="All">
        <listeners>
          <add type="System.Diagnostics.DefaultTraceListener" name="Default">
            <filter type="" />
          </add>
          <add initializeData="D:\WebSites\TEMP\site_messages.svclog"
    type="System.Diagnostics.XmlWriterTraceListener"
    name="ServiceModelMessageLoggingListener" traceOutputOptions="Timestamp">
            <filter type="" />
          </add>
        </listeners>
      </source>
      <source name="System.ServiceModel" switchValue="All"
          propagateActivity="true">
        <listeners>
          <add type="System.Diagnostics.DefaultTraceListener" name="Default">
            <filter type="" />
          </add>
          <add initializeData="D:\WebSites\TEMP\site_tracelog.svclog"
    type="System.Diagnostics.XmlWriterTraceListener"
    name="ServiceModelTraceListener" traceOutputOptions="Timestamp">
            <filter type="" />
          </add>
        </listeners>
      </source>
    </sources>
<system.diagnostics>
	
<system.serviceModel>
    <diagnostics>      
      <messageLogging logMalformedMessages="true"
		  logMessagesAtTransportLevel="true"
                    logEntireMessage="true"           
		  logMessagesAtServiceLevel="true"           
		  maxMessagesToLog="3000"/>
    </diagnostics>
</system.serviceModel>
參考資料:

  1. WCF管理與診斷

  2. Configuring Tracing的Trace Level部分

2011年9月3日 星期六

[ASP.NET]使用反射機制驅動事件

問題:在ASP.NET的Callback發生時,可以同時處理Postback機制的事件,但是Postback的頁面會刷掉Callback的結果,造成畫面閃動的現象。

但是我還是想要由Ccallback執行Poastback的事件。

目標:可以應用在ASP.NET的Web From上,由一個控制項來驅動另一個控制項的事件。注意,在同一個類別中不適用(用一般的方法就好啦)。

方法:1. 註冊一個事件,可以使用

        protected void Button1_Click(object sender, EventArgs e)
        {            
        }

(.aspx 的 AutoEventWireup="true")



或是

        protected void Page_Load(object sender, EventArgs e)
        {
            Button1.Click +=new EventHandler(Button1_Click);
        }


2. 找到畫面上的目標控制項,就是要驅動事件的控制項。例如要驅動Button的Click事件


Button btn = this.Page.FindControl("button1") as Button;


3. 使用反射方法找到Click事件的委託(Delegate),再執行這個事件。

RaiseEvent(btn, "Click", new EventArgs());
private void RaiseEvent(object sender, string eventName, EventArgs args)
{
    PropertyInfo property = sender.GetType().GetProperty("Events",
                                             BindingFlags.Instance |
                                             BindingFlags.NonPublic);
    EventHandlerList ehList = (EventHandlerList)property.GetValue(sender, null);
    FieldInfo fieldInfo = sender.GetType().GetField("Event" + eventName,
                                                  BindingFlags.Static |
                                                  BindingFlags.NonPublic);
    Delegate d = ehList[fieldInfo.GetValue(null)];
    object[] objects = new object[] { sender, new EventArgs() };
    d.DynamicInvoke(objects);
}

 


參考文件:


2011年6月22日 星期三

如何建立樹狀(分層)資料繫結控制項?

樹狀資料繫結控制項一般呈現為樹狀結構,使用的資料來源也是具有樹狀結構的資料,像是XML或是SiteMap等資料。

簡而言之,只要2個步驟:

  1. 繼承自HierarchicalDataBoundControl
  2. 改寫PerformDataBinding(),取得資料來源,並進行資料繫結。

第2步驟相當於一般資料繫結控制項的PerformSelect()和PerformDataBinding()。

為什麼不是使用PerformSelect()和PerformDataBinding()?關鍵在於"樹狀"結構,必須要使用遞迴來處理,一層一層的往下構建。

在細節方面,有幾個地方需要注意:

  1. 如果已經初始化資料繫結控制項,而資料來源在PreRender事件後變更,那麼就必須要執行OnDataPropertyChanged()方法。
    if (Initialized) //BaseDataBoundControl.Initialized
    {
        OnDataPropertyChanged(); //BaseDataBoundControl.OnDataPropertyChanged()
    }

  2. 需使用樹狀列舉IHierarchicalEnumerable介面來處理資料。包括IHierarchicalEnumerable.GetHierarchyData()方法與IHierarchyData介面。
    private void RecurseDataBindInternal(TreeNode RootNode, IHierarchicalEnumerable enumerable, int depth)
    {
        foreach (object item in enumerable)
        {
            IHierarchyData data = enumerable.GetHierarchyData(item);
            ...
        }
    }

  3. 如果有設定DataTextField就使用DataBinder.GetPropertyValue()來取取得屬性,不然就用TypeDescriptor.GetProperties()來做。
    if (DataTextField.Length > 0)
    {
        System.Xml.XmlElement elm = data.Item as System.Xml.XmlElement;
        if (elm.HasAttributes)
        {
            newNode.Text = DataBinder.GetPropertyValue(data, DataTextField, null);
        }
        else
        {
            newNode.Text = "(Family)";
        }
    }
    else
    {
        PropertyDescriptorCollection props = TypeDescriptor.GetProperties(data);
        newNode.Text = string.Empty;
        if (props.Count >= 1)
        {
            if (null != props[0].GetValue(data).ToString())
            {
                newNode.Text = props[0].GetValue(data).ToString();
            }
        }
    }

  4. 遞迴使用自訂義RecurseDataBindInternal()方法來新增結點並進行資料繫結。
    IHierarchicalEnumerable enumerable = view.Select();
    if (enumerable != null)
    {
        Nodes.Clear();
        try
        {
            RecurseDataBindInternal(RootNode, enumerable, 1);
        }
        catch(Exception e)
        {
        }
    }
主要的程式碼如下(已修正MSDN中屬性的BUG):
namespace Samples.AspNet.CS.Controls
{    
    [ToolboxData("<{0}:GeneologyTree runat=server></{0}:GeneologyTree>")]
    public class GeneologyTree:HierarchicalDataBoundControl
    {
        private TreeNode rootNode;
        public TreeNode RootNode
        {
            get
            {
                if (rootNode == null)
                {
                    rootNode = new TreeNode(string.Empty);
                }
                return rootNode;
            }
        }
        private ArrayList nodes;
        public ArrayList Nodes
        {
            get
            {
                if (nodes == null)
                {
                    nodes = new ArrayList();
                }
                return nodes;
            }
        }
        public string DataTextField
        {
            get
            {
                object o = ViewState["DataTextField"];
                return (o == null) ? string.Empty : (string)o;
            }
            set
            {
                ViewState["DataTextField"] = value;
                if (Initialized) //BaseDataBoundControl.Initialized
                {
                    OnDataPropertyChanged(); //BaseDataBoundControl.OnDataPropertyChanged()
                }
            }
        }
        private int _maxDepth = 0;
        protected override void PerformDataBinding()
        {
            base.PerformDataBinding();
            if (!IsBoundUsingDataSourceID && DataSource == null)
            {
                return;
            }
            HierarchicalDataSourceView view = GetData(RootNode.DataPath);
            if (view == null)
            {
                throw new InvalidOperationException("No view returned by data source control.");
            }
            IHierarchicalEnumerable enumerable = view.Select();
            if (enumerable != null)
            {
                Nodes.Clear();
                try
                {
                    RecurseDataBindInternal(RootNode, enumerable, 1);
                }
                catch(Exception e)
                {
                }
            }
        }
        private void RecurseDataBindInternal(TreeNode RootNode, IHierarchicalEnumerable enumerable, int depth)
        {
            foreach (object item in enumerable)
            {
                IHierarchyData data = enumerable.GetHierarchyData(item);
                if (null != data)
                {
                    TreeNode newNode = new TreeNode();
                    RootViewNode rvNode = new RootViewNode();
                    rvNode.Node = newNode;
                    rvNode.Depth = depth;
                    if (DataTextField.Length > 0)
                    {
                        System.Xml.XmlElement elm = data.Item as System.Xml.XmlElement;
                        if (elm.HasAttributes)
                        {
                            newNode.Text = DataBinder.GetPropertyValue(data, DataTextField, null);
                        }
                        else
                        {
                            newNode.Text = "(Family)";
                        }
                    }
                    else
                    {
                        PropertyDescriptorCollection props = TypeDescriptor.GetProperties(data);
                        newNode.Text = string.Empty;
                        if (props.Count >= 1)
                        {
                            if (null != props[0].GetValue(data).ToString())
                            {
                                newNode.Text = props[0].GetValue(data).ToString();
                            }
                        }
                    }
                    nodes.Add(rvNode);
                    if (data.HasChildren)
                    {
                        IHierarchicalEnumerable newEnumerable = data.GetChildren();
                        if (newEnumerable != null)
                        {
                            RecurseDataBindInternal(newNode, newEnumerable, depth + 1);
                        }
                    }
                    if (_maxDepth < depth) _maxDepth = depth;
                }
            }
        }
        protected override void RenderContents(HtmlTextWriter writer)
        {
            writer.Write("<PRE>");
            int currentDepth = 1;
            int currentTextLength = 0;
            foreach (RootViewNode rvNode in Nodes)
            {
                if (rvNode.Depth != currentDepth)
                {
                    writer.WriteLine(""); //子系需換行
                    int halfLine = currentTextLength / 2;
                    string indent = new string(' ', halfLine);
                    writer.WriteLine(indent + "|"); //子系需縮排並加上連接線
                    ++currentDepth;
                    currentTextLength = 0;
                    writer.Write(indent);
                }
                string output = " " + rvNode.Node.Text + " ";
                writer.Write(output);
                currentTextLength = currentTextLength + output.Length;
            }
            writer.Write("</PRE>");
        }
    }
}
使用的XML檔案(geneology.xml):
<?xml version="1.0" encoding="utf-8" ?>
<family>
    <member title="great-grandfather">
        <member title="grandfather" >
            <member title="child" />
            <member title="father" >
                <member title="son" />
            </member>
        </member>
    </member>
</family>
測試的網頁:
<cc1:GeneologyTree ID="GeneologyTree1" runat="server" 
        DataSourceID="XmlDataSource1" DataTextField="title" />
<asp:XmlDataSource ID="XmlDataSource1" runat="server" 
    DataFile="~/App_Data/geneology.xml"></asp:XmlDataSource>

 


測試的結果:


image


參考資料: