Why is my code not being reached after an await statment? - wpf

For some reason I cannot figure out, the code beyond my await statement in the following code is never reached. Why?
public class FileSystemUpdateSource : IUpdateSource
{
private ObservableCollection<UpdatePackage> _packages = new ObservableCollection<UpdatePackage>();
private readonly DirectoryInfo _importDirectory = null;
public FileSystemUpdateSource(DirectoryInfo a_importDirectory)
{
#region Argument Validation
if (a_importDirectory == null)
throw new ArgumentNullException("a_importDirectory");
#endregion
_importDirectory = a_importDirectory;
Refresh();
}
public ObservableCollection<UpdatePackage> Packages
{
get { return _packages; }
}
public async void RefreshAsync()
{
var task = new Task<IEnumerable<UpdatePackage>>(CreatePackages);
var packages = await task;
// *** Code not reached ***
Packages.Clear();
Packages.AddRange(packages);
}
public void Refresh()
{
var packages = CreatePackages();
Packages.Clear();
Packages.AddRange(packages);
}
private IEnumerable<UpdatePackage> CreatePackages()
{
var packageFiles = from packageFile in _importDirectory.EnumerateFiles().AsParallel()
where FileIsZip(packageFile)
select new UpdatePackage(packageFile);
return packageFiles;
}
private bool FileIsZip(FileInfo a_file)
{
using (var fin = a_file.OpenRead())
{
byte[] firstBytes = new byte[5];
fin.Read(firstBytes, 0, 5);
if (firstBytes[0] == 80 && firstBytes[1] == 75 && firstBytes[2] == 3 && firstBytes[3] == 4 && firstBytes[4] == 20)
return true;
else
return false;
}
}
}
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
var importRoot = ApplicationSettings.Current.GetValue("ImportRoot") as System.IO.DirectoryInfo;
IUpdateSource updateSource = new FileSystemUpdateSource(importRoot);
updateSource.RefreshAsync();
// *** Code IS reached here. ***
}
Edits
The answer thanks to add task.Start() to my code right before the await like so (thanks to Reed Copsey's answer):
/// <summary>
/// Refresh the list of available update packages (asynchronously).
/// </summary>
public async Task RefreshAsync()
{
var task = new Task<IEnumerable<UpdatePackage>>(CreatePackages);
task.Start();
var packages = await task;
Packages.Clear();
Packages.AddRange(packages);
}

You're creating your Task incorrectly.
When you call new Task, it doesn't start the task. You should use Task.Run instead:
var task = Task.Run(CreatePackages);
This can be simplified to:
public async void RefreshAsync()
{
var packages = await Task.Run(CreatePackages);

Related

How to call wpf function via JS when visiting https link

I need to execute a function defined in wpf project, which is called from JS in a https web page.
The demo project of all codes is here: https://github.com/tomxue/WebViewIssueInWpf
JS part:
The web page link is https://cmsdev.lenovo.com.cn/musichtml/leHome/weather/index.html?date=&city=&mark=0&speakerId=&reply=
And it contains below line:
<script src="js/index.js" type="text/javascript" charset="utf-8"></script>
And js/index.js contains below code:
setTitle(dataObject.city + weekDay(dataObject.date) +"天气" )
setTitle() is defined below: uses method of window.external.notify()
function setTitle(_str){
try{
wtjs.setTitle(_str)
}catch(e){
console.log(_str)
window.external.notify(_str);
}
}
The function window.external.notify() will call wpf function via ScriptNotify().
WPF part:
For the WebView inside of the wpf project
this.wv.IsScriptNotifyAllowed = true;
this.wv.ScriptNotify += Wv_ScriptNotify;
And
private void Wv_ScriptNotify(object sender, Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT.WebViewControlScriptNotifyEventArgs e)
{
textBlock.Text = e.Value;
}
Problems:
(1)
The problem here is if the web page uses https://, then the above function Wv_ScriptNotify() in wpf will not be fired. But if the web page link uses http://, then the above function Wv_ScriptNotify() in wpf can be fired.
Why and how to solve it?
Update:
2020-3-2 17:25:55, tested just now, https works. I do not know what causes https does not work previously
(2)
JS in the web page uses a object wtjs (defined by ourselves and work well with an UWP project using JSBridge).
And I want to use a similiar method to UWP, using a bridge so that I can add multiple funtions/interfaces for JS to call. The disadvantage of ScriptNotify() is that only one interface is usable.
To achieve it, I make below code, which is commented out now.
wv.RegisterName("wtjs", new myBridge());
And more functions are defined as below
public class myBridge
{
public void SetTitle(string title)
{
Debug.WriteLine("SetTitle is executing...title = {0}", title);
}
public void PlayTTS(string tts)
{
Debug.WriteLine("PlayTTS is executing...tts = {0}", tts);
}
}
While in JS side, corresponding functions will be called.
wtjs.playTTS(tts)
wtjs.setTitle(_str)
But in fact wpf side did not work, while the UWP project using JSBridge works with the web link(so web page and JS script are workable). How to achieve it?
(3)
The above two problems are solved by DK Dhilip's answer already.
But a new problem is found. Please check my GitHub code, update it to latest commit.
https://github.com/tomxue/WebViewIssueInWpf
I put a TextBlock onto WebView and expect to see the text floating on the web content. But in fact, the text is covered by the WebView. Why and how to solve it?
Thanks!
For Problem (1, 2)
HTTPS link worked fine for me, maybe the page is too slow to load?
According to Microsoft (source), only ScriptNotify is supported in WebView:
Can I inject native objects into my WebViewControl content?
No.
Neither the WebBrower (Internet Explorer) ObjectForScripting property
nor the WebView (UWP) AddWebAllowedObject method are supported in
WebViewControl. As a workaround, you can use window.external.notify/
ScriptNotify and JavaScript execution to communicate between the
layers, for example:
https://github.com/rjmurillo/WebView_AddAllowedWebObjectWorkaround
But the above suggested workaround solution seems to work differently to your expectation, so I just implement my own solution to emulate the JSBridge convention you have expected.
My custom solution is not battle-tested, it might break in some edge cases but it seems to work fine in few simple tests.
What's supported:
Multiple bridge objects
JS to C# method call
JS to C# get/set property
C# Usage:
// Add
webView.AddWebAllowedObject("wtjs", new MyBridge(this));
webView.AddWebAllowedObject("myBridge", new MyOtherBridge());
// Remove
webView.RemoveWebAllowedObject("wtjs");
JS Usage:
// Call C# object method (no return value)
wtjs.hello('hello', 'world', 666);
myBridge.saySomething('天猫精灵,叫爸爸!');
// Call C# object method (return value)
wtjs.add(10, 20).then(function (result) { console.log(result); });
// Get C# object property
wtjs.backgroundColor.then(function (color) { console.log(color); });
// Set C# object property
wtjs.niubility = true;
Code
WebViewExtensions.cs
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
using Microsoft.Toolkit.Wpf.UI.Controls;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Text;
namespace WpfApp3
{
// Source: https://github.com/dotnet/orleans/issues/1269#issuecomment-171233788
public static class JsonHelper
{
private static readonly Type[] _specialNumericTypes = { typeof(ulong), typeof(uint), typeof(ushort), typeof(sbyte) };
public static object ConvertWeaklyTypedValue(object value, Type targetType)
{
if (targetType == null)
throw new ArgumentNullException(nameof(targetType));
if (value == null)
return null;
if (targetType.IsInstanceOfType(value))
return value;
var paramType = Nullable.GetUnderlyingType(targetType) ?? targetType;
if (paramType.IsEnum)
{
if (value is string)
return Enum.Parse(paramType, (string)value);
else
return Enum.ToObject(paramType, value);
}
if (paramType == typeof(Guid))
{
return Guid.Parse((string)value);
}
if (_specialNumericTypes.Contains(paramType))
{
if (value is BigInteger)
return (ulong)(BigInteger)value;
else
return Convert.ChangeType(value, paramType);
}
if (value is long || value is double)
{
return Convert.ChangeType(value, paramType);
}
return value;
}
}
public enum WebViewInteropType
{
Notify = 0,
InvokeMethod = 1,
InvokeMethodWithReturn = 2,
GetProperty = 3,
SetProperty = 4
}
public class WebAllowedObject
{
public WebAllowedObject(WebView webview, string name)
{
WebView = webview;
Name = name;
}
public WebView WebView { get; private set; }
public string Name { get; private set; }
public ConcurrentDictionary<(string, WebViewInteropType), object> FeaturesMap { get; } = new ConcurrentDictionary<(string, WebViewInteropType), object>();
public EventHandler<WebViewControlNavigationCompletedEventArgs> NavigationCompletedHandler { get; set; }
public EventHandler<WebViewControlScriptNotifyEventArgs> ScriptNotifyHandler { get; set; }
}
public static class WebViewExtensions
{
public static bool IsNotification(this WebViewControlScriptNotifyEventArgs e)
{
try
{
var message = JsonConvert.DeserializeObject<dynamic>(e.Value);
if (message["___magic___"] != null)
{
return false;
}
}
catch (Exception) { }
return true;
}
public static void AddWebAllowedObject(this WebView webview, string name, object targetObject)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentNullException(nameof(name));
if (targetObject == null)
throw new ArgumentNullException(nameof(targetObject));
if (webview.Tag == null)
{
webview.Tag = new ConcurrentDictionary<string, WebAllowedObject>();
}
else if (!(webview.Tag is ConcurrentDictionary<string, WebAllowedObject>))
{
throw new InvalidOperationException("WebView.Tag property is already being used for other purpose.");
}
var webAllowedObjectsMap = webview.Tag as ConcurrentDictionary<string, WebAllowedObject>;
var webAllowedObject = new WebAllowedObject(webview, name);
if (webAllowedObjectsMap.TryAdd(name, webAllowedObject))
{
var objectType = targetObject.GetType();
var methods = objectType.GetMethods();
var properties = objectType.GetProperties();
var jsStringBuilder = new StringBuilder();
jsStringBuilder.Append("(function () {");
jsStringBuilder.Append("window['");
jsStringBuilder.Append(name);
jsStringBuilder.Append("'] = {");
jsStringBuilder.Append("__callback: {},");
jsStringBuilder.Append("__newUuid: function () { return ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, function (c) { return (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16); }); },");
foreach (var method in methods)
{
if (!method.IsSpecialName)
{
if (method.ReturnType == typeof(void))
{
webAllowedObject.FeaturesMap.TryAdd((method.Name, WebViewInteropType.InvokeMethod), method);
}
else
{
webAllowedObject.FeaturesMap.TryAdd((method.Name, WebViewInteropType.InvokeMethodWithReturn), method);
}
var parameters = method.GetParameters();
var parametersInString = string.Join(",", parameters.Select(x => x.Position).Select(x => "$$" + x.ToString()));
jsStringBuilder.Append(method.Name);
jsStringBuilder.Append(": function (");
jsStringBuilder.Append(parametersInString);
jsStringBuilder.Append(") {");
if (method.ReturnType != typeof(void))
{
jsStringBuilder.Append("var callbackId = window['" + name + "'].__newUuid();");
}
jsStringBuilder.Append("window.external.notify(JSON.stringify({");
jsStringBuilder.Append("source: '");
jsStringBuilder.Append(name);
jsStringBuilder.Append("',");
jsStringBuilder.Append("target: '");
jsStringBuilder.Append(method.Name);
jsStringBuilder.Append("',");
jsStringBuilder.Append("parameters: [");
jsStringBuilder.Append(parametersInString);
jsStringBuilder.Append("]");
if (method.ReturnType != typeof(void))
{
jsStringBuilder.Append(",");
jsStringBuilder.Append("callbackId: callbackId");
}
jsStringBuilder.Append("}), ");
jsStringBuilder.Append((method.ReturnType == typeof(void)) ? (int)WebViewInteropType.InvokeMethod : (int)WebViewInteropType.InvokeMethodWithReturn);
jsStringBuilder.Append(");");
if (method.ReturnType != typeof(void))
{
jsStringBuilder.Append("var promise = new Promise(function (resolve, reject) {");
jsStringBuilder.Append("window['" + name + "'].__callback[callbackId] = { resolve, reject };");
jsStringBuilder.Append("});");
jsStringBuilder.Append("return promise;");
}
jsStringBuilder.Append("},");
}
}
jsStringBuilder.Append("};");
foreach (var property in properties)
{
jsStringBuilder.Append("Object.defineProperty(");
jsStringBuilder.Append("window['");
jsStringBuilder.Append(name);
jsStringBuilder.Append("'], '");
jsStringBuilder.Append(property.Name);
jsStringBuilder.Append("', {");
if (property.CanRead)
{
webAllowedObject.FeaturesMap.TryAdd((property.Name, WebViewInteropType.GetProperty), property);
jsStringBuilder.Append("get: function () {");
jsStringBuilder.Append("var callbackId = window['" + name + "'].__newUuid();");
jsStringBuilder.Append("window.external.notify(JSON.stringify({");
jsStringBuilder.Append("source: '");
jsStringBuilder.Append(name);
jsStringBuilder.Append("',");
jsStringBuilder.Append("target: '");
jsStringBuilder.Append(property.Name);
jsStringBuilder.Append("',");
jsStringBuilder.Append("callbackId: callbackId,");
jsStringBuilder.Append("parameters: []");
jsStringBuilder.Append("}), ");
jsStringBuilder.Append((int)WebViewInteropType.GetProperty);
jsStringBuilder.Append(");");
jsStringBuilder.Append("var promise = new Promise(function (resolve, reject) {");
jsStringBuilder.Append("window['" + name + "'].__callback[callbackId] = { resolve, reject };");
jsStringBuilder.Append("});");
jsStringBuilder.Append("return promise;");
jsStringBuilder.Append("},");
}
if (property.CanWrite)
{
webAllowedObject.FeaturesMap.TryAdd((property.Name, WebViewInteropType.SetProperty), property);
jsStringBuilder.Append("set: function ($$v) {");
jsStringBuilder.Append("window.external.notify(JSON.stringify({");
jsStringBuilder.Append("source: '");
jsStringBuilder.Append(name);
jsStringBuilder.Append("',");
jsStringBuilder.Append("target: '");
jsStringBuilder.Append(property.Name);
jsStringBuilder.Append("',");
jsStringBuilder.Append("parameters: [$$v]");
jsStringBuilder.Append("}), ");
jsStringBuilder.Append((int)WebViewInteropType.SetProperty);
jsStringBuilder.Append(");");
jsStringBuilder.Append("},");
}
jsStringBuilder.Append("});");
}
jsStringBuilder.Append("})();");
var jsString = jsStringBuilder.ToString();
webAllowedObject.NavigationCompletedHandler = (sender, e) =>
{
var isExternalObjectCustomized = webview.InvokeScript("eval", new string[] { "window.external.hasOwnProperty('isCustomized').toString();" }).Equals("true");
if (!isExternalObjectCustomized)
{
webview.InvokeScript("eval", new string[] { #"
(function () {
var originalExternal = window.external;
var customExternal = {
notify: function (message, type = 0) {
if (type === 0) {
originalExternal.notify(message);
} else {
originalExternal.notify(JSON.stringify({
___magic___: true,
type: type,
interop: message
}));
}
},
isCustomized: true
};
window.external = customExternal;
})();" });
}
webview.InvokeScript("eval", new string[] { jsString });
};
webAllowedObject.ScriptNotifyHandler = (sender, e) =>
{
try
{
var message = JsonConvert.DeserializeObject<dynamic>(e.Value);
if (message["___magic___"] != null)
{
var interopType = (WebViewInteropType)message.type;
var interop = JsonConvert.DeserializeObject<dynamic>(message.interop.ToString());
var source = (string)interop.source.ToString();
var target = (string)interop.target.ToString();
var parameters = (object[])interop.parameters.ToObject<object[]>();
if (interopType == WebViewInteropType.InvokeMethod)
{
if (webAllowedObjectsMap.TryGetValue(source, out WebAllowedObject storedWebAllowedObject))
{
if (storedWebAllowedObject.FeaturesMap.TryGetValue((target, interopType), out object methodObject))
{
var method = (MethodInfo)methodObject;
var parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray();
var convertedParameters = new object[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
convertedParameters[i] = JsonHelper.ConvertWeaklyTypedValue(parameters[i], parameterTypes[i]);
}
method.Invoke(targetObject, convertedParameters);
}
}
}
else if (interopType == WebViewInteropType.InvokeMethodWithReturn)
{
var callbackId = interop.callbackId.ToString();
if (webAllowedObjectsMap.TryGetValue(source, out WebAllowedObject storedWebAllowedObject))
{
if (storedWebAllowedObject.FeaturesMap.TryGetValue((target, interopType), out object methodObject))
{
var method = (MethodInfo)methodObject;
var parameterTypes = method.GetParameters().Select(x => x.ParameterType).ToArray();
var convertedParameters = new object[parameters.Length];
for (var i = 0; i < parameters.Length; i++)
{
convertedParameters[i] = JsonHelper.ConvertWeaklyTypedValue(parameters[i], parameterTypes[i]);
}
var invokeResult = method.Invoke(targetObject, convertedParameters);
webview.InvokeScript("eval", new string[] { string.Format("window['{0}'].__callback['{1}'].resolve({2}); delete window['{0}'].__callback['{1}'];", source, callbackId, JsonConvert.SerializeObject(invokeResult)) });
}
}
}
else if (interopType == WebViewInteropType.GetProperty)
{
var callbackId = interop.callbackId.ToString();
if (webAllowedObjectsMap.TryGetValue(source, out WebAllowedObject storedWebAllowedObject))
{
if (storedWebAllowedObject.FeaturesMap.TryGetValue((target, interopType), out object propertyObject))
{
var property = (PropertyInfo)propertyObject;
var getResult = property.GetValue(targetObject);
webview.InvokeScript("eval", new string[] { string.Format("window['{0}'].__callback['{1}'].resolve({2}); delete window['{0}'].__callback['{1}'];", source, callbackId, JsonConvert.SerializeObject(getResult)) });
}
}
}
else if (interopType == WebViewInteropType.SetProperty)
{
if (webAllowedObjectsMap.TryGetValue(source, out WebAllowedObject storedWebAllowedObject))
{
if (storedWebAllowedObject.FeaturesMap.TryGetValue((target, interopType), out object propertyObject))
{
var property = (PropertyInfo)propertyObject;
property.SetValue(targetObject, JsonHelper.ConvertWeaklyTypedValue(parameters[0], property.PropertyType));
}
}
}
}
}
catch (Exception ex)
{
// Do nothing
}
};
webview.NavigationCompleted += webAllowedObject.NavigationCompletedHandler;
webview.ScriptNotify += webAllowedObject.ScriptNotifyHandler;
}
else
{
throw new InvalidOperationException("Object with the identical name is already exist.");
}
}
public static void RemoveWebAllowedObject(this WebView webview, string name)
{
if (string.IsNullOrWhiteSpace(name))
throw new ArgumentNullException(nameof(name));
var allowedWebObjectsMap = webview.Tag as ConcurrentDictionary<string, WebAllowedObject>;
if (allowedWebObjectsMap != null)
{
if (allowedWebObjectsMap.TryRemove(name, out WebAllowedObject webAllowedObject))
{
webview.NavigationCompleted -= webAllowedObject.NavigationCompletedHandler;
webview.ScriptNotify -= webAllowedObject.ScriptNotifyHandler;
webview.InvokeScript("eval", new string[] { "delete window['" + name + "'];" });
}
}
}
}
}
MainWindow.xaml.cs
using Microsoft.Toolkit.Win32.UI.Controls.Interop.WinRT;
using System;
using System.Diagnostics;
using System.Windows;
namespace WpfApp3
{
public partial class MainWindow : Window
{
public class MyBridge
{
private readonly MainWindow _window;
public MyBridge(MainWindow window)
{
_window = window;
}
public void setTitle(string title)
{
Debug.WriteLine(string.Format("SetTitle is executing...title = {0}", title));
_window.setTitle(title);
}
public void playTTS(string tts)
{
Debug.WriteLine(string.Format("PlayTTS is executing...tts = {0}", tts));
}
}
public MainWindow()
{
this.InitializeComponent();
this.wv.IsScriptNotifyAllowed = true;
this.wv.ScriptNotify += Wv_ScriptNotify;
this.wv.AddWebAllowedObject("wtjs", new MyBridge(this));
this.Loaded += MainPage_Loaded;
}
private void Wv_ScriptNotify(object sender, WebViewControlScriptNotifyEventArgs e)
{
if (e.IsNotification())
{
Debug.WriteLine(e.Value);
}
}
private void setTitle(string str)
{
textBlock.Text = str;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
this.wv.Source = new Uri("https://cmsdev.lenovo.com.cn/musichtml/leHome/weather/index.html?date=&city=&mark=0&speakerId=&reply=");
}
}
}
Result
Screenshot:
For Problem (3)
According to (1, 2, 3), it is impossible to overlay UI elements on top of WebView/WebBrowser control.
Luckily there is an alternative solution called CefSharp which is based on Chromium web browser and would be good enough for your use case, plus the background animation worked (which doesn't work in original WebView control).
However, there is no perfect solution; WPF design view is unusable with CefSharp (showing Invalid Markup error), but the program will just compile and run. Also, the project can only be built with either x86 or x64 option, AnyCPU will not work.
MainWindow.xaml
<Window
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:cefSharp="clr-namespace:CefSharp.Wpf;assembly=CefSharp.Wpf"
x:Class="WpfApp3.MainWindow"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid x:Name="grid">
<cefSharp:ChromiumWebBrowser x:Name="wv" HorizontalAlignment="Left" Height="405" Margin="50,0,0,0" VerticalAlignment="Top" Width="725" RenderTransformOrigin="-0.45,-0.75" />
<TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="30,30,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Height="60" Width="335"/>
</Grid>
</Window>
MainWindow.xaml.cs
using CefSharp;
using System.Diagnostics;
using System.Windows;
namespace WpfApp3
{
public partial class MainWindow : Window
{
public class MyBridge
{
private readonly MainWindow _window;
public MyBridge(MainWindow window)
{
_window = window;
}
public void setTitle(string title)
{
Debug.WriteLine(string.Format("SetTitle is executing...title = {0}", title));
_window.setTitle(title);
}
public void playTTS(string tts)
{
Debug.WriteLine(string.Format("PlayTTS is executing...tts = {0}", tts));
}
}
public MainWindow()
{
this.InitializeComponent();
this.wv.JavascriptObjectRepository.Register("wtjs", new MyBridge(this), true, new BindingOptions() { CamelCaseJavascriptNames = false });
this.wv.FrameLoadStart += Wv_FrameLoadStart;
this.Loaded += MainPage_Loaded;
}
private void Wv_FrameLoadStart(object sender, FrameLoadStartEventArgs e)
{
if (e.Url.StartsWith("https://cmsdev.lenovo.com.cn/musichtml/leHome/weather"))
{
e.Browser.MainFrame.ExecuteJavaScriptAsync("CefSharp.BindObjectAsync('wtjs');");
}
}
private void setTitle(string str)
{
this.Dispatcher.Invoke(() =>
{
textBlock.Text = str;
});
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
this.wv.Address = "https://cmsdev.lenovo.com.cn/musichtml/leHome/weather/index.html?date=&city=&mark=0&speakerId=&reply=";
}
}
}
Screenshot:

.net c# WebChannelFactory keepAliveEnabled=false with webHttpBehavior

I create WebChannelFactory. I need set KeepAlive=false.
I found only one solution use CustomBinding. and set property keepAliveEnabled to false.
I use custom behavior for my factory also.
Code:
static CustomBinding GetBinding(MySettings serviceSettings = null)
{
var customBinding = new CustomBinding();
HttpTransportBindingElement transportBindingElement = new HttpTransportBindingElement();
transportBindingElement.KeepAliveEnabled = false;
transportBindingElement.MaxBufferSize = 0x07000000;
transportBindingElement.MaxReceivedMessageSize = 0x07000000;
if (serviceSettings != null)
{
customBinding.SendTimeout = serviceSettings.SendTimeout;
}
customBinding.Elements.Add(transportBindingElement);
return customBinding;
}
var customBinding = GetBinding(serviceSettings);
WebChannelFactory<TChannel> factory = new WebChannelFactory<TChannel>(customBinding, new Uri(url));
factory.Endpoint.Behaviors.Add(new MyWebHttpBehavior(userId));
class MyWebHttpBehavior : IEndpointBehavior
{
private int _userId;
public MyWebHttpBehavior(int userId)
{
_userId = userId;
}
public void AddBindingParameters(ServiceEndpoint serviceEndpoint, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{ }
public void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.ClientRuntime behavior)
{
behavior.MessageInspectors.Add(new MyClientMessageInspector(_userId));
}
public void ApplyDispatchBehavior(ServiceEndpoint serviceEndpoint, System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{ }
public void Validate(ServiceEndpoint serviceEndpoint)
{ }
}
In current situation i get error "does not have a Binding with the None MessageVersion".

why Dispatcher.invoke() not working?

whenever a change is occured changes i call this set_filelist_inventory(). and send a new list to this function but UI is not upadating.
public partial class Inventory : UserControl
{
public List<String> file_list = new List<String>();
public void set_filelist_inventory(List<string> x)
{
if (file_list.SequenceEqual(x)) { }
else
{
file_list = x;
Dispatcher.Invoke(() =>
{
listview1.ItemsSource = file_list;
});
}
}
public Inventory()
{
InitializeComponent();
file_list = General.GetFileList();
Discover d = new Discover();
d.send(d);
listview1.ItemsSource = file_list;
}
}
I tried removing if else block. it still didn't worked.

Query CRM data in Silverlight

I am building a silverlight app for CRM 2011 and I was wondering what the best way to retrieve data from the CRM system is.
I have linked in my organisation as a service reference and am able to access that. I have seen a few different ways to retrieve data but they all seem rather complicated. Is there anything like what we can use in plugins such as a fetch XMl query or a simple Service.Retrieve method?
Thanks
If you add a service reference to your project you can use LINQ to query the datasets.
You can download the CSDL from Developer Resources under the customisation area.
private FelineSoftContext context;
private System.String serverUrl;
private DataServiceCollection<SalesOrder> _orders;
public MainPage()
{
InitializeComponent();
serverUrl = (String)GetContext().Invoke("getServerUrl");
//Remove the trailing forward slash returned by CRM Online
//So that it is always consistent with CRM On Premises
if (serverUrl.EndsWith("/"))
serverUrl = serverUrl.Substring(0, serverUrl.Length - 1);
Uri ODataUri = new Uri(serverUrl + "/xrmservices/2011/organizationdata.svc/", UriKind.Absolute);
context = new FelineSoftContext(ODataUri) { IgnoreMissingProperties = true };
var orders = from ord in context.SalesOrderSet
orderby ord.Name
select new SalesOrder
{
Name = ord.Name,
SalesOrderId = ord.SalesOrderId
};
_orders = new DataServiceCollection<SalesOrder>();
_orders.LoadCompleted += _orders_LoadCompleted;
_orders.LoadAsync(orders);
}
void _orders_LoadCompleted(object sender, LoadCompletedEventArgs e)
{
if (e.Error == null)
{
if (_orders.Continuation != null)
{
_orders.LoadNextPartialSetAsync();
}
else
{
OrderLookup.ItemsSource = _orders;
OrderLookup.DisplayMemberPath = "Name";
OrderLookup.SelectedValuePath = "Id";
}
}
}
You will also need to add a method in your class as below:
private static ScriptObject GetContext()
{
ScriptObject xrmProperty = (ScriptObject)HtmlPage.Window.GetProperty("Xrm");
if (null == xrmProperty)
{
//It may be that the global context should be used
try
{
ScriptObject globalContext = (ScriptObject)HtmlPage.Window.Invoke("GetGlobalContext");
return globalContext;
}
catch (System.InvalidOperationException)
{
throw new InvalidOperationException("Property \"Xrm\" is null and the Global Context is not available.");
}
}
ScriptObject pageProperty = (ScriptObject)xrmProperty.GetProperty("Page");
if (null == pageProperty)
{
throw new InvalidOperationException("Property \"Xrm.Page\" is null");
}
ScriptObject contextProperty = (ScriptObject)pageProperty.GetProperty("context");
if (null == contextProperty)
{
throw new InvalidOperationException("Property \"Xrm.Page.context\" is null");
}
return contextProperty;
}
You will need to add an additional class library and put the following code within it. Change the class name to match the Context you exported:
partial class FelineSoftContext
{
#region Methods
partial void OnContextCreated()
{
this.ReadingEntity += this.OnReadingEntity;
this.WritingEntity += this.OnWritingEntity;
}
#endregion
#region Event Handlers
private void OnReadingEntity(object sender, ReadingWritingEntityEventArgs e)
{
ODataEntity entity = e.Entity as ODataEntity;
if (null == entity)
{
return;
}
entity.ClearChangedProperties();
}
private void OnWritingEntity(object sender, ReadingWritingEntityEventArgs e)
{
ODataEntity entity = e.Entity as ODataEntity;
if (null == entity)
{
return;
}
entity.RemoveUnchangedProperties(e.Data);
entity.ClearChangedProperties();
}
#endregion
}
public abstract class ODataEntity
{
private readonly Collection<string> ChangedProperties = new Collection<string>();
public ODataEntity()
{
EventInfo info = this.GetType().GetEvent("PropertyChanged");
if (null != info)
{
PropertyChangedEventHandler method = new PropertyChangedEventHandler(this.OnEntityPropertyChanged);
//Ensure that the method is not attached and reattach it
info.RemoveEventHandler(this, method);
info.AddEventHandler(this, method);
}
}
#region Methods
public void ClearChangedProperties()
{
this.ChangedProperties.Clear();
}
internal void RemoveUnchangedProperties(XElement element)
{
const string AtomNamespace = "http://www.w3.org/2005/Atom";
const string DataServicesNamespace = "http://schemas.microsoft.com/ado/2007/08/dataservices";
const string DataServicesMetadataNamespace = DataServicesNamespace + "/metadata";
if (null == element)
{
throw new ArgumentNullException("element");
}
List<XElement> properties = (from c in element.Elements(XName.Get("content", AtomNamespace)
).Elements(XName.Get("properties", DataServicesMetadataNamespace)).Elements()
select c).ToList();
foreach (XElement property in properties)
{
if (!this.ChangedProperties.Contains(property.Name.LocalName))
{
property.Remove();
}
}
}
private void OnEntityPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (!this.ChangedProperties.Contains(e.PropertyName))
{
this.ChangedProperties.Add(e.PropertyName);
}
}
#endregion
}
I am will suggest you to use Silvercrmsoap , it's very easy to use. I have used this in my silverlight projects.

Silverlight Async Method Chaining (Possible gotchas?)

I am working on a 'proof of concept' Silverlight 4 project and am learning the way of THE ASYNC. I have stopped fighting the urge to implement some pseudo-synchronous smoke and mirrors technique. I am going to learn to stop worrying and love THE ASYNC.
Most of the time I just use a BusyIndicator while async methods are running and all is good but I have run into a few situations where I need to call methods sequentially. I put together this example and it works. But in my experience... if it works... there is something wrong with it.
When is this going to blow up in my face or steal my wife or date one of my daughters?
Is there a better way to do this?
The Code:
public class CustomPage : Page
{
static readonly object _AsyncMethodChain_Lock = new object();
private Dictionary<Action<object>, string> _AsyncMethodChain = new Dictionary<Action<object>, string>();
public Dictionary<Action<object>, string> AsyncMethodChain
{
get { lock (_AsyncMethodChain_Lock) { return this._AsyncMethodChain; } }
set { lock (_AsyncMethodChain_Lock) { this._AsyncMethodChain = value; } }
}
private void CustomPage_Loaded(object sender, RoutedEventArgs e)
{
if (!System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
{
var user = this.SecurityProvider.UserObject as TimeKeeper.UserServiceReference.User;
if (user == null)
return;
this.AsyncMethodChain.Add(
data =>
{
var userServiceClient = new UserServiceClient();
userServiceClient.GetCompleted +=
(send, arg) =>
{
var userViewSource = this.Resources["userViewSource"] as CollectionViewSource;
userViewSource.Source = new List<UserServiceReference.User>(new UserServiceReference.User[1] { arg.Result });
userViewSource.View.MoveCurrentToPosition(0);
this.AsyncMethodChain.ExecuteNext(arg.Result.UserID, this.BusyIndicator);
};
userServiceClient.GetAsync(user.UserID);
},
"Loading user..."
);
this.AsyncMethodChain.Add(
data =>
{
var userID = (int)data;
var timeLogServiceClient = new TimeLogServiceClient();
timeLogServiceClient.FindByUserIDCompleted +=
(send, arg) =>
{
var timeLogViewSource = this.Resources["timeLogViewSource"] as CollectionViewSource;
timeLogViewSource.Source = arg.Result;
this.AsyncMethodChain.ExecuteNext(null, this.BusyIndicator);
};
timeLogServiceClient.FindByUserIDAsync(userID);
},
"Loading time logs..."
);
this.AsyncMethodChain.ExecuteNext(null, this.BusyIndicator);
}
}
}
public static class Extensions
{
public static void ExecuteNext(this Dictionary<Action<object>, string> methods, object data, BusyIndicator busyIndicator)
{
if (methods.Count <= 0)
{
busyIndicator.BusyContent = "";
busyIndicator.IsBusy = false;
return;
}
else
{
var method = methods.Keys.ToList<Action<object>>()[0];
busyIndicator.BusyContent = methods[method];
busyIndicator.IsBusy = true;
}
methods.ExecuteNext(data);
}
public static void ExecuteNext(this Dictionary<Action<object>, string> methods, object data)
{
var method = methods.Keys.ToList<Action<object>>()[0];
methods.Remove(method);
method(data);
}
}
What you have sine looks pretty good, but if you are still worried about the callsequence i would sugest that you create a new method in your webservice which will call the other four in whatever sequence you need them to and call this new method from your silverlight application.
I dont see the need for you to do that as your current implemenation is pretty good as well.

Resources