Tab Handling in Mvvmcross with MvxTabsFragmentActivity and MvxCachingFragmentStatePagerAdapter - tabs

I am writing an Android app with a tab. I was following the old method in the sample "FragmentSample". It was working fine but I am evaluating to switch to viewpager.
In FragmentSample:
TabViewModel creates an instance of viewmodel for each individual tab
(Vm1, Vm2...).
In TabView, each tab fragment (Tab1Fragment,
Tab2Fragment...) are explicitly associated to the viewmodel (Vm1,
Vm2...) created in TabViewModel.
It is perfect as I could do some navigation initialization to Vm1,
Vm2 in TabViewModel.
public class TabViewModel : BaseViewModel
{
public TabViewModel()
{
Vm1 = Mvx.IocConstruct<FirstTabViewModel>();
Vm2 = Mvx.IocConstruct<SecondTabViewModel>();
Vm3 = Mvx.IocConstruct<ThirdTabViewModel>();
}
public BaseViewModel Vm1 { get; set; }
public BaseViewModel Vm2 { get; set; }
public BaseViewModel Vm3 { get; set; }
}
public class TabView : MvxTabsFragmentActivity
{
public TabViewModel TabViewModel
{
get { return (TabViewModel)base.ViewModel; }
}
public TabView()
: base(Resource.Layout.Page_TabView, Resource.Id.actualtabcontent)
{
}
protected override void AddTabs(Bundle args)
{
AddTab<Tab1Fragment>("Tab1", "Tab 1", args, TabViewModel.Vm1);
AddTab<Tab2Fragment>("Tab2", "Tab 2", args, TabViewModel.Vm2);
// note that
AddTab<Tab3Fragment>("Tab3.1", "Tab 3.1", args, TabViewModel.Vm3);
AddTab<Tab3Fragment>("Tab3.2", "Tab 3.2", args, TabViewModel.Vm3);
AddTab<Tab3BigFragment>("Tab3.3", "Tab 3.3", args, TabViewModel.Vm3);
}
}
In the latest sample project "Example" in MvvmCross-All:
ExampleViewPagerStateViewModel create an instance of RecyclerViewModel
ExampleViewPagerStateFragment defines the tabs (RecyclerView
1...5) with MvxCachingFragmentStatePagerAdapter.
When MvxCachingFragmentStatePagerAdapter is executed, another
instance of RecyclerViewModel will be created
RecyclerViewModel created in ExampleViewPagerStateViewModel seems to
be completely irrelevant to the tab built. I commented out the
creation in ExampleViewPagerStateViewModel and there was no change to
the app behavior.
RecyclerViewModel was created twice. It is the same in
ExampleViewPagerFragment in the same project, and in the old version
of this sample XPlatformMenus.
public class ExampleViewPagerStateViewModel
: MvxViewModel
{
public RecyclerViewModel Recycler { get; private set; }
public ExampleViewPagerStateViewModel()
{
Recycler = new RecyclerViewModel();
}
}
public class ExampleViewPagerStateFragment : BaseStateFragment<ExampleViewPagerStateViewModel>
{
protected override int FragmentId => Resource.Layout.fragment_example_viewpager_state;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = base.OnCreateView(inflater, container, savedInstanceState);
var viewPager = view.FindViewById<ViewPager>(Resource.Id.viewpager);
if (viewPager != null)
{
var fragments = new List<MvxCachingFragmentStatePagerAdapter.FragmentInfo>
{
//new MvxCachingFragmentStatePagerAdapter.FragmentInfo("RecyclerView 1", typeof (RecyclerViewFragment),
// typeof (RecyclerViewModel)),
//new MvxCachingFragmentStatePagerAdapter.FragmentInfo("RecyclerView 2", typeof (RecyclerViewFragment),
// typeof (RecyclerViewModel)),
//new MvxCachingFragmentStatePagerAdapter.FragmentInfo("RecyclerView 3", typeof (RecyclerViewFragment),
// typeof (RecyclerViewModel)),
//new MvxCachingFragmentStatePagerAdapter.FragmentInfo("RecyclerView 4", typeof (RecyclerViewFragment),
// typeof (RecyclerViewModel)),
new MvxCachingFragmentStatePagerAdapter.FragmentInfo("RecyclerView 5", typeof (RecyclerViewFragment),
typeof (RecyclerViewModel))
};
viewPager.Adapter = new MvxCachingFragmentStatePagerAdapter(Activity, ChildFragmentManager, fragments);
}
var tabLayout = view.FindViewById<TabLayout>(Resource.Id.tabs);
tabLayout.SetupWithViewPager(viewPager);
return view;
}
}
My questions are:
What is the usage of creating RecyclerViewModel in
ExampleViewPagerStateViewModel in "Example"?
In FragmentSample, Tab3.1 Tab3.2 Tab3.3 are sharing the same Vm3. Can
I do the same thing with ViewPager? Is there any way I can specify
the tab view (RecyclerView 1...5) to associate to the
RecyclerViewModel created in ExampleViewPagerStateViewModel but not a
new instance?
Thanks.

Just found that it could be done by changing the third parameter of FragmentInfo to Recycler created in RecyclerViewModel. The sample should make the change.

Related

My static variables in my Blazor Server app are keeping their values, even if I refresh the page or even I close the tab and login again. Why?

I have a Blazor server app. Some variables on a specific razor page (main.razor) are defined as static because I want that these variables keep their values when the client navigates to other pages in the same project and comes back again to main.razor. So far it is working good.
But when I refresh the complete page, or even close the tab and reopen my app (login again), I see that the static variables still keep their values. How can prevent this? Of course I want that the values return to their default values (like 0 or ""), when the client makes a login or refreshes the page with F5. How can I do that?
I have defined the related variables in the following way:
private static StringBuilder log = new StringBuilder();
public static string testvar1= "";
public static int testvar2= 0;
Statics exist for the lifetime of the application instance which explains the behaviour you see.
You need to be maintaining state. At one end of the spectrum you can implement a State Management system such as Fluxor. At the other just create a user class, set it up as a service and inject it as a Scoped Service. Or you can build a middle-of-the-road solution.
This is mine.
A generic UIStateService that maintains a Dictionary of (state)objects against a Guid.
public class UIStateService
{
private Dictionary<Guid, object> _stateItems = new Dictionary<Guid, object>();
public void AddStateData(Guid Id, object value)
{
if (_stateItems.ContainsKey(Id))
_stateItems[Id] = value;
else
_stateItems.Add(Id, value);
}
public void ClearStateData(Guid Id)
{
if (_stateItems.ContainsKey(Id))
_stateItems.Remove(Id);
}
public bool TryGetStateData<T>(Guid Id, out T? value)
{
value = default;
if (Id == Guid.Empty)
return false;
var isdata = _stateItems.ContainsKey(Id);
var val = isdata
? _stateItems[Id]
: default;
if (val is T)
{
value = (T)val;
return true;
}
return false;
}
}
Set it up as a service:
builder.Services.AddScoped<UIStateService>();
Next define a simple template ComponentBase page that contains the common page code:
using Blazr.UI;
using Microsoft.AspNetCore.Components;
namespace BlazorApp2.Pages
{
public class StatePage : ComponentBase
{
// this provides a guid for this specific page during the lifetime of the application runtime
// we use this as the reference to store the state data against
private static Guid RouteId = Guid.NewGuid();
[Inject] protected UIStateService UIStateService { get; set; } = default!;
protected void SaveState<T>(T state) where T : class, new()
{
if (RouteId != Guid.Empty)
this.UIStateService.AddStateData(RouteId, state);
}
protected bool GetState<T>( out T value) where T : class, new()
{
value = new T();
if (RouteId != Guid.Empty && this.UIStateService.TryGetStateData<T>(RouteId, out T? returnedState))
{
value = returnedState ?? new T();
return true;
}
else
return false;
}
}
}
And use it in a page:
#page "/"
#inherits StatePage
<PageTitle>Index</PageTitle>
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
<div class="p-2">
<button class="btn btn-primary" #onclick=SetData>Set Data</button>
</div>
<div class="p-3 text-primary">
State Time : #stateData.StateTime;
</div>
#code {
private MyStateData stateData = new MyStateData();
protected override void OnInitialized()
{
if (this.GetState<MyStateData>(out MyStateData value))
this.stateData = value;
else
this.SaveState<MyStateData>(this.stateData);
}
private void SetData()
{
this.stateData.StateTime = DateTime.Now.ToLongTimeString();
SaveState<MyStateData>(this.stateData);
}
public class MyStateData
{
public string StateTime { get; set; } = DateTime.Now.ToLongTimeString();
}
}
You can now navigate around the application and the state will be maintained for the page.
You can apply an observer/notification pattern to the state object to trigger automatic state updates if you wish.

How to get data by json on xamarin android

public async override void OnActivityCreated (Bundle savedInstanceState)
{
base.OnActivityCreated (savedInstanceState);
lst = View.FindViewById<ListView> (Resource.Id.lstHome);
var result = await json.GetStringbyJson ("https://api-v2.soundcloud.com/explore/Popular+Music?tag=out-of-experiment&limit=20&linked_partitioning=1");
if (result != null)
{
var items = Newtonsoft.Json.JsonConvert.DeserializeObject<TrackModel.RootObject> (result);
lst.Adapter = new TrackAdapter(Activity, items.tracks);
}
}
public class TrackAdapter:BaseAdapter
{
LayoutInflater _inflater;
List<TrackModel.Track> _tracks;
public TrackAdapter(Context context, List<TrackModel.Track> tracks)
{
_inflater=LayoutInflater.FromContext(context);
_tracks=tracks;
}
public override TrackModel.Track this[int index]
{
get{ return _tracks [index]; }
}
public override int Count{
get{ return _tracks.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView,ViewGroup parent)
{
View view = convertView ?? _inflater.Inflate (Resource.Layout.ExploreFragment, parent, false);
var track = _tracks [position];
var viewHolder = view.Tag as TrackViewHolder;
if (viewHolder == null) {
viewHolder.Title = view.FindViewById<TextView> (Resource.Id.textviewItems);
viewHolder.SubTitle = view.FindViewById<TextView> (Resource.Id.textviewSubItem);
viewHolder.Image = view.FindViewById<ImageView> (Resource.Id.image);
view.Tag = viewHolder;
}
viewHolder.Title.Text = track.title;
viewHolder.SubTitle.Text = track.track_type;
Android.Net.Uri uri = Android.Net.Uri.Parse (track.artwork_url);
viewHolder.Image.SetImageURI(uri);
return view;
}
}
public class TrackViewHolder:Java.Lang.Object
{
public TextView Title{ get; set;}
public TextView SubTitle{get;set;}
public ImageView Image{ get; set;}
}
public override TrackModel.Track this[int index]. It get a error is makred as an overdie but no suitable indexer found to overide.
I want to take data from json up listview on xamarin android.
If it is unviersal app then it easy to use.
The way you want to set the adapter for your listview will not work that way.
Setting the adapter property of the listview inside the foreach loop is totally wrong. The same applies to your textviews.
You need to implement a custom adapter that loads a layout for each of your track list item. Your custom adapter could look like the following example I've written out of my mind with out further testing. But it implements the required methods a custom adapter needs to implement.
The important part is the GetView method that returns your track layout every time the listview ask for a new item to represent. To keep the app memory down it uses the ViewHolder pattern, which isn't required if you want to use the RecycleView.
public class TrackAdapter : BaseAdapter<Tracks>
{
LayoutInflater _inflater;
List<Tracks> _tracks;
public TrackAdapter(Context context, List<Tracks) tracks)
{
_inflater = LayoutInflater.FromContext(context);
_tracks = tracks;
}
public override Tracks this [int index]
{
get { return _tracks[index]; }
}
public override int Count
{
get { return _tracks.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view = convertView ?? _inflater.Inflate(Resource.Layout.TrackListItem, parent, false);
var track = _tracks[position];
var viewHolder = view.Tag as TrackViewHolder;
if (viewHolder == null)
{
viewHolder = new TrackViewHolder();
viewHolder.Title = view.FindViewById<TextView>(Resource.Id.textviewItems);
viewHolder.Subtitle = view.FindViewById<TextView>(Resource.Id.textviewSubItems);
viewHolder.Image = view.FindViewById<ImageView>(Resource.Id.image);
view.Tag = viewHolder;
}
viewHolder.Title.Text = track.title;
viewHolder.SubTitle.Text = track.track_type;
viewHolder.Image.SetImageURI(Uri(track.artwork_url));
return view;
}
class TrackViewHolder : Java.Lang.Object
{
public TextView Title { get; set; }
public TextView SubTitle { get; set; }
public ImageView Image { get; set; }
}
}
The layout will contain your title, subtitle and image and could easily build with a normal layout file.
In your fragment you then create a new instance for TrackAdapter pass the context and the list of tracks you want to be shown in the listview.
public override void OnActivityCreated (Bundle savedInstanceState)
{
base.OnActivityCreated (savedInstanceState);
lst = View.FindViewById<ListView> (Resource.Id.lstHome);
var result = json.GetStringbyJson ("https://api-v2.soundcloud.com/explore/Popular+Music?tag=out-of-experiment&limit=20&linked_partitioning=1");
if (result != null)
{
var items = JsonConvert.DeserializeObject<TrackModel.RootObject> (result);
lst.Adapter = new TrackAdapter(Activity, items.tracks);
}
}

How can I do JSON serializer ignore navigation properties?

I am exactly in the same case that this question:
How do I make JSON.NET ignore object relationships?
I see the proposed solution and I know I must use a Contract Revolver, and I also see the code of the Contract Resolver, but I do not know how to use it.
Should I use it in the WebApiConfig.vb?
Should I modify my Entity Model anyway?
It is a useful question👍 and I hope this help:
A)
If you have created your models manually (without Entity Framework), mark the relation properties as virtual first.
If your models were created by EF, It has already done it for you and each Relation Property is marked as virtual, as seen below:
Sample class:
public class PC
{
public int FileFolderId {get;set;}
public virtual ICollection<string> Libs { get; set; }
public virtual ICollection<string> Books { get; set; }
public virtual ICollection<string> Files { get; set; }
}
B)
Those relation properties can now be ignored by the JSON serializer by using the following ContractResolver for JSON.NET:
CustomResolver:
class CustomResolver : DefaultContractResolver
{
private readonly List<string> _namesOfVirtualPropsToKeep=new List<string>(new String[]{});
public CustomResolver(){}
public CustomResolver(IEnumerable<string> namesOfVirtualPropsToKeep)
{
this._namesOfVirtualPropsToKeep = namesOfVirtualPropsToKeep.Select(x=>x.ToLower()).ToList();
}
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
{
JsonProperty prop = base.CreateProperty(member, memberSerialization);
var propInfo = member as PropertyInfo;
if (propInfo != null)
{
if (propInfo.GetMethod.IsVirtual && !propInfo.GetMethod.IsFinal
&& !_namesOfVirtualPropsToKeep.Contains(propInfo.Name.ToLower()))
{
prop.ShouldSerialize = obj => false;
}
}
return prop;
}
}
C)
Finally, to serialize your model easily use the above ContractResolver. Set it up like this:
// -------------------------------------------------------------------
// Serializer settings
JsonSerializerSettings settings = new JsonSerializerSettings
{
// ContractResolver = new CustomResolver();
// OR:
ContractResolver = new CustomResolver(new []
{
nameof(PC.Libs), // keep Libs property among virtual properties
nameof(PC.Files) // keep Files property among virtual properties
}),
PreserveReferencesHandling = PreserveReferencesHandling.None,
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
Formatting = Formatting.Indented
};
// -------------------------------------------------------------------
// Do the serialization and output to the console
var json = JsonConvert.SerializeObject(new PC(), settings);
Console.WriteLine(json);
// -------------------------------------------------------------------
// We can see that "Books" filed is ignored in the output:
// {
// "FileFolderId": 0,
// "Libs": null,
// "Files": null
// }
Now, all the navigation (relation) properties [virtual properties] will be ignored automatically except you keep some of them by determine them in your code.😎
Live DEMO
Thanks from #BrianRogers for his answer here.
If you are using Newtonsoft.Json
Mark field with
Newtonsoft.Json.JsonIgnore
Instead of
System.Text.Json.Serialization.JsonIgnore

Binding StringElement (MT.D) with MvvmCross

We are using some MT.D StringElements, and their Value Property is bound to properties in the ViewModel.
The initial value is correctly shown but when the ViewModel changes some values and triggers PropertyChanged then the StringElements contain the good value but the display is not refreshed.
If we scroll the Controller or touch the StringElement then it is refreshed: the correct value is displayed.
Do you have any idea?
This is our ViewController
public class ContactView : MvxDialogViewController
{
public override void ViewDidLoad()
{
base.ViewDidLoad();
var bindings = this.CreateInlineBindingTarget<ContactViewModel> ();
Root = new RootElement()
{
new Section()
{
new StringElement("Company Name").Bind(bindings, vm => vm.CompanyName)
}
}
}
}
This is our ViewModel (simplified)
public class ContactViewModel : MvxViewModel
{
private string companyName;
public string CompanyName{
get{return companyName;}
set{companyName = value; RaisePropertyChanged(() => CompanyName);}
}
public async Task Init(string id)
{
var contact = await someService.SomeMethodAsync();
CompanyName = contact.CompanyName;
}
}
I found two solutions to my problem:
If I use UIView.Transition to replace the content then, in the new View, nothing is refreshed when I change the ViewModel (unless I scroll or tap it) UNLESS if the ViewModel properties have some default value non null and non empty
If I don't transition but use another method like this one to replace the content:
Sample code
MasterNavigationController.PopToRootViewController(false);
MasterNavigationController.ViewControllers = new UIViewController[] { viewController };
In this case the content is replaced and the view is refreshed when a ViewModel property changes: everything works correctly in this case.
I tried a viewmodel like:
public class FirstViewModel
: MvxViewModel
{
private Timer _timer;
private int _count;
public FirstViewModel()
{
_timer = new Timer(DoThis, null, 1000, 1000);
}
private void DoThis(object state)
{
_count++;
TextProperty = _count.ToString();
}
private string _textProperty = "T";
public string TextProperty
{
get { return _textProperty; }
set { _textProperty = value; RaisePropertyChanged(() => TextProperty); }
}
}
with a dialog view defined like:
Root = new RootElement("Example Root")
{
new Section("Debut in:")
{
new EntryElement("Login", "Enter Login name").Bind(bindings, vm => vm.TextProperty)
},
new Section("Debug out:")
{
new StringElement("Value is:").Bind(bindings, vm => vm.TextProperty),
};
It worked fine - ticking up every second...

Force WCF Rest client to use Json deserializer regardless of content-type

How to force WCF Rest client to use Json deserializer regardless of content-type?
I am invoking a REST based web service through WCF.
The service returns JSON body, but has content-type "Application/xml". The WCF framework is now giving me the XmlException.
public class MessageFormatter : IClientMessageFormatter
{
private readonly IClientMessageFormatter _formatter;
public MessageFormatter(IClientMessageFormatter formatter)
{
_formatter = formatter;
}
public object DeserializeReply(System.ServiceModel.Channels.Message message, object[] parameters)
{
return _formatter.DeserializeReply(message, parameters);
}
}
that _formatter.DeserializeReply is throwing XmlException. I can't find any example anywhere to force json deserialization on reply.
Edit - The "message" object when moused over is throwing "{... Error reading body: System.Xml.XmlException: The data at the root level is invalid. Line 1, position 1. ...}"
That same object in another one of my project that communicate with a different REST service (Picasa web services) has a what seems like a xml serialised version of JSON object?? So the problem seems further up the stream. I need to find where this object is originating from. I'll go play around with MessageEncoder class.
Edit - (Adding more info)
public class MyBinding : WebHttpBinding
{
public MyBinding(WebHttpSecurityMode mode)
: base(mode)
{
}
public override BindingElementCollection CreateBindingElements()
{
var result = base.CreateBindingElements();
var replacements = result.OfType<MessageEncodingBindingElement>().ToList();
foreach (var messageEncodingBindingElement in replacements)
{
var index = result.IndexOf(messageEncodingBindingElement);
result.Remove(messageEncodingBindingElement);
result.Insert(index, new MyMessageEncodingBindingElement(messageEncodingBindingElement));
}
return result;
}
}
public class MyMessageEncodingBindingElement : MessageEncodingBindingElement
{
private readonly MessageEncodingBindingElement _element;
public MyMessageEncodingBindingElement(MessageEncodingBindingElement element)
{
_element = element;
}
public override BindingElement Clone()
{
var result = _element.Clone();
if (result is MessageEncodingBindingElement)
return new MyMessageEncodingBindingElement(result as MessageEncodingBindingElement);
return result;
}
public override MessageEncoderFactory CreateMessageEncoderFactory()
{
return new MyMessageEncoderFactory(_element.CreateMessageEncoderFactory());
}
}
The method CreateMessageEncoderFactory() is never called even when the constructor and Clone method are hit when breakpoints are set. Any help? I'm trying to set a custom MessageEncoder and MessageEncoderFactory class to modify the instantiation process of the Message object.
You can use a WebContentTypeMapper for that. That's a property of the WebHttpBinding, and you can customize how the deserialization will be done by the encoder from that binding, including forcing it to always use the JSON deserializer, regardless of the incoming message's Content-Type. The code below shows how this can be done.
public class StackOverflow_13225272
{
[DataContract]
public class Person
{
[DataMember]
public string Name { get; set; }
[DataMember]
public int Age { get; set; }
public override string ToString()
{
return string.Format("Person[Name={0},Age={1}]", Name, Age);
}
}
[ServiceContract]
public interface ITest
{
[WebGet(ResponseFormat = WebMessageFormat.Json)]
Person GetPerson(string responseContentType);
}
public class Service : ITest
{
public Person GetPerson(string responseContentType)
{
WebOperationContext.Current.OutgoingResponse.ContentType = responseContentType;
return new Person { Name = "John Doe", Age = 29 };
}
}
class AllJsonContentTypeMapper : WebContentTypeMapper
{
public override WebContentFormat GetMessageFormatForContentType(string contentType)
{
return WebContentFormat.Json;
}
}
public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
host.Open();
Console.WriteLine("Host opened");
#if USE_NETFX4
// This works on .NET 4.0 and beyond
WebHttpBinding binding = new WebHttpBinding();
binding.ContentTypeMapper = new AllJsonContentTypeMapper();
#else
// This works on .NET 3.5
CustomBinding binding = new CustomBinding(new WebHttpBinding());
binding.Elements.Find<WebMessageEncodingBindingElement>().ContentTypeMapper = new AllJsonContentTypeMapper();
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(binding, new EndpointAddress(baseAddress));
#endif
ChannelFactory<ITest> factory = new ChannelFactory<ITest>(binding, new EndpointAddress(baseAddress));
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
ITest proxy = factory.CreateChannel();
Console.WriteLine("With JSON: {0}", proxy.GetPerson("application/json"));
Console.WriteLine("With XML: {0}", proxy.GetPerson("application/xml"));
Console.Write("Press ENTER to close the host");
Console.ReadLine();
host.Close();
}
}
This might work.
public class ForceJsonClientMessageFormatter : IClientMessageFormatter
{
private readonly DataContractJsonSerializer _jsonSerializer;
public ForceJsonClientMessageFormatter(Type responseType)
{
_jsonSerializer = new DataContractJsonSerializer(responseType);
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
throw new NotImplementedException("This client message formatter is for replies only!");
}
public object DeserializeReply(Message message, object[] parameters)
{
string messageBody = message.GetBody<string>();
using (MemoryStream messageStream = new MemoryStream(Encoding.UTF8.GetBytes(messageBody)))
{
messageStream.Seek(0, SeekOrigin.Begin);
object deserializedObject = _jsonSerializer.ReadObject(messageStream);
return deserializedObject;
}
}
}
public class ForceJsonWebHttpBehavior : WebHttpBehavior
{
protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
return new ForceJsonClientMessageFormatter(operationDescription.Messages[1].Body.ReturnValue.Type);
}
}
I haven't tried it, but I think this will work. You can create a custom IClientMessageFormatter which overwrites the message format to Json, wrap that in a behavior, and then apply that behavior to your client endpoint configuration.
public class ForceJsonClientMessageFormatterDecorator : IClientMessageFormatter
{
private readonly IClientMessageFormatter _decoratedFormatter;
public ForceJsonClientMessageFormatterDecorator(IClientMessageFormatter decoratedFormatter)
{
_decoratedFormatter = decoratedFormatter;
}
public object DeserializeReply(Message message, object[] parameters)
{
message.Properties[WebBodyFormatMessageProperty.Name] = new WebBodyFormatMessageProperty(WebContentFormat.Json);
return _decoratedFormatter.DeserializeReply(message, parameters);
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
{
return _decoratedFormatter.SerializeRequest(messageVersion, parameters);
}
}
public class ForceJsonWebHttpBehavior : WebHttpBehavior
{
protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
IClientMessageFormatter decoratedFormatter = base.GetReplyClientFormatter(operationDescription, endpoint);
return new ForceJsonClientMessageFormatterDecorator(decoratedFormatter);
}
}