WCF - Error Handling - Creator of this service did not specify a Reason - wcf

There are lot of threads with the same error but I could not find a suitable answer so putting it again.
I have a wcf service and I have error handling in place and when I test the service from the wcftestclient all I get is this error info and not the original error that actually occurred.
The creator of this fault did not specify a Reason. Server stack trace: at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc) at ...
I am not sure why its not returning me the actual error. My error handling code is given below.
/// <summary>
/// Gets the holidays.
/// </summary>
/// <returns>List<HolidayProfile></returns>
[OperationContract]
[FaultContract(typeof(ExceptionFaultContract))]
List<HolidayProfile> GetHolidays();
public List<HolidayProfile> GetHolidays()
{
List<HolidayProfile> holidayProfileList = new List<HolidayProfile>();
try
{
customerBL = new CustomerBL(new CustomerRepository());
holidayProfileList = customerBL.GetHolidays();
}
catch (CustomException ex)
{
throw new FaultException<ExceptionFaultContract>(DBHelper.MapException(ex));
}
return holidayProfileList;
}
I have used two custom Exception classes here. One which is derived from Exception and other is the one which will be passed between the service and client
/// <summary>
/// Class CustomException.
/// </summary>
/// <seealso cref="System.Exception" />
public class CustomException : Exception
{
/// <summary>
/// Gets or sets the error code.
/// </summary>
/// <value>The error code.</value>
public string ErrorCode { get; set; }
/// <summary>
/// Gets or sets the error detail.
/// </summary>
/// <value>The error detail.</value>
public string ErrorDescription { get; set; }
/// <summary>
/// Gets or sets the error number.
/// </summary>
/// <value>The error number.</value>
public int ErrorNumber { get; set; }
}
and another which is the datacontract.
/// <summary>
/// Class ExceptionFaultContract.
/// </summary>
[DataContract]
public class ExceptionFaultContract : Exception
{
/// <summary>
/// Gets or sets the error code.
/// </summary>
/// <value>The error code.</value>
[DataMember]
public string ErrorCode { get; set; }
/// <summary>
/// Gets or sets the error number.
/// </summary>
/// <value>The error number.</value>
[DataMember]
public int ErrorNumber { get; set; }
/// <summary>
/// Gets or sets the error description.
/// </summary>
/// <value>The error description.</value>
[DataMember]
public string ErrorDescription { get; set; }
}
My method in the dataacess.
public List<HolidayProfile> GetHolidays()
{
Initializations
try
{
Code here...
}
catch (OracleException ox)
{
throw (DBHelper.HandleOracleException(ox));
}
catch (Exception ex)
{
throw (DBHelper.HandleException1(ex));
}
finally
{
if (connection != null)
connection.Close();
if (command != null)
command = null;
if (dataAdapter != null)
dataAdapter = null;
dataSet.Dispose();
}
return holidayProfileList;
}
My helper class which does the mapping.
/// <summary>
/// Handles the exception.
/// </summary>
/// <param name="ex">The ex.</param>
/// <returns>ExceptionFaultContract.</returns>
public static CustomException HandleException(Exception ex)
{
CustomException customException = new CustomException();
customException.ErrorDescription = ex.Message;
return customException;
}
/// <summary>
/// Maps the exception.
/// </summary>
/// <param name="ex">The ex.</param>
/// <returns>ExceptionFaultContract.</returns>
public static ExceptionFaultContract MapException(CustomException ex)
{
ExceptionFaultContract exceptionFaultContract = new ExceptionFaultContract();
exceptionFaultContract.ErrorCode = ex.ErrorCode;
exceptionFaultContract.ErrorNumber = ex.ErrorNumber;
exceptionFaultContract.ErrorDescription = ex.ErrorDescription;
return exceptionFaultContract;
}
I m getting an error if I m using the ExceptionFaultContract to be returned from data access ("Error : should be a type derived from exception") and If I try to derive the same from Exception another error saying it cannot be both serializable and derived member or something like that..
I tried lot of things nothing is clear to me...could somebody please help.

Related

In Xamarin.Forms.UWP, Unable to set Automation ID to custom control, if not used SetNativeControl() method

I am creating a custom control in Xamarin.Forms which is inherited from TemplatedView and using the application in all three platforms. For testing purposes, I need to set Automation ID for all native controls.
Usually, by using AutomationPeer we can set Automation ID to native UWP control from the UWP renderer project. But, in my custom control, I get this. Control is null always.
So, I can’t set Automation ID in UWP renderer class as Forms TemplatedView is a FrameworkElement in UWP.
My query is,
How to get a native element (this.Control) in UWP renderer project for Xamarin.Forms TemplatedView.
How to set Automation Id to native UWP control if this.Control is null.
The code snippet is provided below:
class FormsCustomLayout : TemplatedView
{
public static readonly BindableProperty InputViewProperty =
BindableProperty.Create(nameof(InputView), typeof(View), typeof(FormsCustomLayout), null, BindingMode.Default, null, OnInputViewChanged);
private static void OnInputViewChanged(BindableObject bindable, object oldValue, object newValue)
{
(bindable as FormsCustomLayout).OnInputViewChanged(oldValue, newValue);
}
private void OnInputViewChanged(object oldValue, object newValue)
{
var oldView = (View)oldValue;
if (oldView != null)
{
if (this.contentGrid.Children.Contains(oldView))
{
oldView.SizeChanged -= OnInputViewSizeChanged;
oldView.BindingContext = null;
this.contentGrid.Children.Remove(oldView);
}
}
var newView = (View)newValue;
if (newView != null)
{
newView.SizeChanged += OnInputViewSizeChanged;
newView.VerticalOptions = LayoutOptions.CenterAndExpand;
newView.HeightRequest = 60;
this.contentGrid.Children.Add(newView);
}
}
private void OnInputViewSizeChanged(object sender, EventArgs e)
{
}
public View InputView
{
get { return (View)GetValue(InputViewProperty); }
set { SetValue(InputViewProperty, value); }
}
internal void UpdateText(object text)
{
this.Text = (string)text;
}
private readonly Grid contentGrid = new Grid();
public FormsCustomLayout()
{
this.ControlTemplate = new ControlTemplate(typeof(StackLayout));
((StackLayout)Children[0]).Children.Add(this.contentGrid);
if (InputView != null)
{
this.contentGrid.Children.Add(InputView);
}
}
internal string Text { get; private set; }
}
The custom renderer code snippet is provided below:
class FormsCustomLayoutRenderer : ViewRenderer<FormsCustomLayout, FrameworkElement>
{
/// <summary>
/// Method that is called when the automation id is set.
/// </summary>
/// <param name="id">The automation id.</param>
protected override void SetAutomationId(string id)
{
if (this.Control == null)
{
base.SetAutomationId(id);
}
else
{
this.SetAutomationPropertiesAutomationId(id);
this.Control.SetAutomationPropertiesAutomationId(id);
}
}
/// <summary>
/// Provide automation peer for the control
/// </summary>
/// <returns>The TextInputLayout view automation peer.</returns>
protected override AutomationPeer OnCreateAutomationPeer()
{
if (this.Control == null)
{
return new FormsCustomLayoutAutomationPeer(this);
}
return new FormsCustomLayoutAutomationPeer(this.Control);
}
protected override void OnElementChanged(ElementChangedEventArgs<FormsCustomLayout> e)
{
base.OnElementChanged(e);
var element = e.NewElement;
}
}
internal class FormsCustomLayoutAutomationPeer : FrameworkElementAutomationPeer
{
/// <summary>
/// Initializes a new instance of the <see cref="FormsCustomLayoutRenderer"/> class.
/// </summary>
/// <param name="owner">FormsCustomLayout View control</param>
public FormsCustomLayoutAutomationPeer(FrameworkElement owner) : base(owner)
{
}
/// <summary>
/// Describe the control type
/// </summary>
/// <returns>The control type.</returns>
protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Custom;
}
/// <summary>
/// Describe the class name.
/// </summary>
/// <returns>The Class Name.</returns>
protected override string GetClassNameCore()
{
return "FormsCustomLayout";
}
}
Demo sample attached here: FormsCustomControl
Please share your idea or solution to set Automation ID.
Regards,
Bharathiraja.

Selenium Tests Failing when running them all in one go

I have the below code which initiates the browser in each test. However the first Test runs OK correctly starting the browser. The second Test on wards start to fail
public class BrowserFactory
{
private static IWebDriver driver;
public static IWebDriver Driver
{
get
{
if (driver == null)
throw new NullReferenceException("The WebDriver browser instance was not initialized. You should first call the method InitBrowser.");
return driver;
}
set
{
driver = value;
}
}
public static void InitBrowser(string browserName)
{
switch (browserName)
{
case "Firefox":
if (driver == null)
{
driver = new FirefoxDriver();
driver.Manage().Window.Maximize();
}
break;
case "IE":
if (driver == null)
{
driver = new InternetExplorerDriver();
driver.Manage().Window.Maximize();
}
break;
case "Chrome":
if (driver == null)
{
driver = new ChromeDriver();
driver.Manage().Window.Maximize();
}
break;
}
}
And this is how I initiate and quit browser in each test
[TestFixture]
public class AdminUserHasAccessToDashboard
{
[SetUp]
public void GotoHomePage()
{
BrowserFactory.InitBrowser("Chrome");
BrowserFactory.LoadApplication(ConfigurationManager.AppSettings["URL"]);
}
[TearDown]
public void Quit()
{
BrowserFactory.CloseAllDrivers();
}
It looks like you are probably setting driver instance in your first test and other tests are false on if (driver == null), so other instances will never be created. Your code is incomplete so I can't be sure, but it seems to be an issue here.
How are you closing your driver after first test is completed?
Include the method mentioned below in your browser factory and call at the end of each test, or set up before the tests are run then use the single instance for each test and tear down at the end.
public static void Quit(){
if (Driver!= null)
Driver.Quit();
}
I use a static "Driver" class which is referenced in each test class (not each test case)
I include this in each class(they are nunit C# tests)
#region Initialise and clean up
[OneTimeSetUp]
public void Init()
{
Driver.Initialise();
}
[OneTimeTearDown]
public void CleanUp()
{
Driver.Quit();
}
#endregion
Then my driver class is as below, the appstate region wont apply but can be edited to suit your needs.
using System;
using System.Threading;
using OpenQA.Selenium;
using OpenQA.Selenium.Firefox;
using AdaptiveAds_TestFramework.PageFrameworks;
namespace AdaptiveAds_TestFramework.Helpers
{
/// <summary>
/// Contains functionality for browser automation.
/// </summary>
public static class Driver
{
#region Variables
private static IWebDriver _instance;
private static Period _waitPeriod;
#endregion//Variables
#region Properties
/// <summary>
/// Browser automation object.
/// </summary>
public static IWebDriver Instance
{
get { return _instance; }
set { _instance = value; SetWait(WaitPeriod); }
}
/// <summary>
/// Duration automation framework must wait before throwing an error.
/// </summary>
public static Period WaitPeriod
{
get { return _waitPeriod; }
set
{
_waitPeriod = value;
SetWait(_waitPeriod);
}
}
#endregion//Properties
#region Methods
#region set-up and tear-down
/// <summary>
/// Sets up a new automation object.
/// </summary>
public static void Initialise()
{
Quit();
Instance = new FirefoxDriver(new FirefoxBinary(ConfigData.FireFoxPath), new FirefoxProfile());
Instance.Manage().Window.Maximize();
WaitPeriod = ConfigData.DefaultWaitPeriod;
}
/// <summary>
/// Disposes of the automation object.
/// </summary>
public static void Quit()
{
if (Instance != null)
Instance.Quit();
}
#endregion //set-up and tear-down
#region NavigableLocations
/// <summary>
/// Navigate browser to a given location.
/// </summary>
/// <param name="location">Location to navigate to.</param>
/// <param name="logInIfNeeded">Logs in if authentication is required.</param>
/// <param name="errorIfNotReached">Errors if the location was not reached.</param>
public static void GoTo(Location location, bool logInIfNeeded, bool errorIfNotReached)
{
// Navigate browser to the location.
Instance.Navigate().GoToUrl(Helper.RouteUrl(location));
Thread.Sleep(500);// wait for system to navigate
bool needToLogIn = false;
if (logInIfNeeded)
{
try
{
IsAt(Location.Login);
needToLogIn = true;
}
catch
{
// Not at login page so Login not needed.
}
if (needToLogIn)
{
LoginPage.LoginAs(ConfigData.Username).WithPassword(ConfigData.Password).Login();
GoTo(location, false, errorIfNotReached);
}
}
if (errorIfNotReached)
{
IsAt(location);
}
}
/// <summary>
/// Ensures the Driver is at the specified location, throws a WebDriverException if at another location.
/// </summary>
/// <param name="location">Location to check the browser is at.</param>
public static void IsAt(Location location)
{
string expected = Helper.RouteUrl(location);
string actual = Instance.Url;
// Check the browser is at the correct location.
if (actual != expected)
{
// Driver is not at the specified location.
throw new WebDriverException("Incorrect location.",
new InvalidElementStateException(
"The given location did not match the browser." +
" Expected \"" + expected + "\" Actual \"" + actual + "\""));
}
ActionWait(Period.None, CheckForLavarelError);
}
/// <summary>
/// Ensures the Driver is not at the specified location, throws a WebDriverException if at the location.
/// </summary>
/// <param name="location">Location to check the browser is not at.</param>
public static void IsNotAt(Location location)
{
string expected = Helper.RouteUrl(location);
string actual = Instance.Url;
// Check the browser is not at the correct location.
if (actual == expected)
{
// Driver is at the specified location.
throw new WebDriverException("Incorrect location.",
new InvalidElementStateException(
"The given location matched the browser."));
}
}
#endregion//NavigableLocations
#region WaitHandling
/// <summary>
/// Performs an action with a temporary wait period.
/// </summary>
/// <param name="waitPeriod">Period to wait while executing action.</param>
/// <param name="action">Action to execute.</param>
public static void ActionWait(Period waitPeriod, Action action)
{
// Store the current wait period
Period previousPeriod = WaitPeriod;
// Run task with given wait period
SetWait(waitPeriod);
action();
// Revert to the old wait period
SetWait(previousPeriod);
}
/// <summary>
/// Updates the Automation instance wait period.
/// </summary>
/// <param name="waitPeriod">New wait period.</param>
private static void SetWait(Period waitPeriod)
{
int miliseconds;
ConfigData.WaitPeriods.TryGetValue(waitPeriod, out miliseconds);
// Set the drivers instance to use the wait period.
if (Instance != null)
{
Instance.Manage().Timeouts().ImplicitlyWait(TimeSpan.FromMilliseconds(miliseconds));
}
}
#endregion//WaitHandling
#region AppState
/// <summary>
/// Checks that the website hasn't crashed from the back end.
/// </summary>
public static void CheckForLavarelError()
{
bool error = false;
try
{
Instance.FindElement(By.ClassName("exception_message"));
error = true;
}
catch
{
// all good, could not find error content.
}
if (error)
{
throw new Exception("Lavarel threw an error");
}
}
/// <summary>
/// Clicks on the main menu button to open or close the main menu.
/// </summary>
public static void OpenCloseMenuBar()
{
try
{
IWebElement menuButton = Instance.FindElement(By.Name(ConfigData.MainMenuButtonName));
menuButton.Click();
}
catch (Exception e)
{
throw new NoSuchElementException("Could not find the Menu button element.", e);
}
}
/// <summary>
/// Asserts the logged in state agents the parameter.
/// </summary>
/// <param name="checkLoggedIn">Parameter to check agents logged in state.</param>
public static void LoggedIn(bool checkLoggedIn)
{
OpenCloseMenuBar();
bool signInFound;
bool signOutFound;
bool isLoggedIn = false;
try { Instance.FindElement(By.Name(ConfigData.SignInName)); signInFound = true; }
catch { signInFound = false; }
try { Instance.FindElement(By.Name(ConfigData.SignOutName)); signOutFound = true; }
catch { signOutFound = false; }
if (!signInFound && !signOutFound)
{
throw new ElementNotVisibleException("Unable to assert state due to unavailability of SignIn/Out links.");
}
if (signOutFound) isLoggedIn = true;
if (signInFound) isLoggedIn = false;
if (isLoggedIn != checkLoggedIn)
{
throw new Exception($"Logged in Expected: {checkLoggedIn} Actual: {isLoggedIn}");
}
}
/// <summary>
/// Signs out of the system.
/// </summary>
/// <param name="errorIfAlreadySignedOut">Determines whether to throw an error if already signed out.</param>
public static void SignOut(bool errorIfAlreadySignedOut = true)
{
try
{
LoggedIn(true);
}
catch (Exception)
{
if (errorIfAlreadySignedOut)
{
throw;
}
return;
}
IWebElement signOut = Instance.FindElement(By.Name(ConfigData.SignOutName));
signOut.Click();
Thread.Sleep(500);// wait for system to logout
}
#endregion//AppState
#endregion//Methods
}
}

In WCF BehaviorExtension BeforeSendRequest method is not getting called?I m stuck please help me...below is code

Here is code for the which I m trying.I want to modify the request packet of service. Genarally the request header,body and action
[CustomBehavior]
public class Service1 : IService1
{
.....
......
}
public class CustomHeader : MessageHeader
{
.....
.....
....
}
/// <summary>
/// This class is used to inspect the message and headers on the server side,
/// This class is also used to intercept the message on the
/// client side, before/after any request is made to the server.
/// </summary>
public class CustomMessageInspector : IClientMessageInspector, IDispatchMessageInspector
{
#region Message Inspector of the Service
/// <summary>
/// This method is called on the server when a request is received from the client.
/// </summary>
/// <param name="request"></param>
/// <param name="channel"></param>
/// <param name="instanceContext"></param>
/// <returns></returns>
public object AfterReceiveRequest(ref Message request,
IClientChannel channel, InstanceContext instanceContext)
{
// Create a copy of the original message so that we can mess with it.
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
Message messageCopy = buffer.CreateMessage();
// Read the custom context data from the headers
ServiceHeader customData = CustomHeader.ReadHeader(request);
// Add an extension to the current operation context so
// that our custom context can be easily accessed anywhere.
ServerContext customContext = new ServerContext();
if (customData != null)
{
customContext.KerberosID = customData.KerberosID;
customContext.SiteminderToken = customData.SiteminderToken;
}
OperationContext.Current.IncomingMessageProperties.Add(
"CurrentContext", customContext);
return null;
}
/// <summary>
/// This method is called after processing a method on the server side and just
/// before sending the response to the client.
/// </summary>
/// <param name="reply"></param>
/// <param name="correlationState"></param>
public void BeforeSendReply(ref Message reply, object correlationState)
{
// Do some cleanup
OperationContext.Current.Extensions.Remove(ServerContext.Current);
}
#endregion
#region Message Inspector of the Consumer
/// <summary>
/// This method will be called from the client side just before any method is called.
/// </summary>
/// <param name="request"></param>
/// <param name="channel"></param>
/// <returns></returns>
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
// Prepare the request message copy to be modified
MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
request = buffer.CreateMessage();
ServiceHeader customData = new ServiceHeader();
customData.KerberosID = ClientContext.KerberosID;
customData.SiteminderToken = ClientContext.SiteminderToken;
CustomHeader header = new CustomHeader(customData);
// Add the custom header to the request.
request.Headers.Add(header);
return null;
}
/// <summary>
/// This method will be called after completion of a request to the server.
/// </summary>
/// <param name="reply"></param>
/// <param name="correlationState"></param>
public void AfterReceiveReply(ref Message reply, object correlationState)
{
}
#endregion
}
/// <summary>
/// This class will act as a custom context in the client side to hold the context information.
/// </summary>
public class ClientContext
{
public static string EmployeeID;
public static string WindowsLogonID;
public static string KerberosID;
public static string SiteminderToken;
}
/// <summary>
/// This custom behavior class is used to add both client and server inspectors to
/// the corresponding WCF end points.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class CustomBehavior : Attribute, IEndpointBehavior
{
#region IEndpointBehavior Members
void IEndpointBehavior.AddBindingParameters(ServiceEndpoint endpoint,
System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
{
return;
}
void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint,
System.ServiceModel.Dispatcher.ClientRuntime clientRuntime)
{
CustomMessageInspector inspector = new CustomMessageInspector();
clientRuntime.MessageInspectors.Add(inspector);
}
void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint,
System.ServiceModel.Dispatcher.EndpointDispatcher endpointDispatcher)
{
ChannelDispatcher channelDispatcher = endpointDispatcher.ChannelDispatcher;
if (channelDispatcher != null)
{
foreach (EndpointDispatcher ed in channelDispatcher.Endpoints)
{
CustomMessageInspector inspector = new CustomMessageInspector();
ed.DispatchRuntime.MessageInspectors.Add(inspector);
}
}
}
void IEndpointBehavior.Validate(ServiceEndpoint endpoint) { return; }
#endregion
}
public class MyBehaviorExtensionElement : BehaviorExtensionElement
{
public MyBehaviorExtensionElement() { }
public override Type BehaviorType
{
get { return typeof(CustomBehavior); }
}
protected override object CreateBehavior()
{
return new CustomBehavior();
}
}
You need to add the extension to either your code or configuration: (config example below):
<extensions>
<behaviorExtensions>
<add name="yourMessageInspector"
type="MyBehaviorExtensionElement" />
</behaviorExtensions>
</extensions>
The following references should provide the background you need:
https://msdn.microsoft.com/en-us/library/ms730137%28v=vs.110%29.aspx
wcf :custom message inspector does not get wired up

ContentControl and static ressources

I try to extend my app with the missing CharacterEllipsis for TextBlocks in my Windows Phone 8 app. I found a nice solution here: http://nerdplusart.com/texttrimming-textblock-for-silverlight/.
So I implemented this in my code:
public class DynamicTextBlock : ContentControl
{
#region Text (DependencyProperty)
/// <summary>
/// Gets or sets the Text DependencyProperty. This is the text that will be displayed.
/// </summary>
public string Text
{
get { return (string)GetValue(TextProperty); }
set { SetValue(TextProperty, value); }
}
public static readonly DependencyProperty TextProperty =
DependencyProperty.Register("Text", typeof(string), typeof(DynamicTextBlock),
new PropertyMetadata(null, new PropertyChangedCallback(OnTextChanged)));
private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DynamicTextBlock)d).OnTextChanged(e);
}
protected virtual void OnTextChanged(DependencyPropertyChangedEventArgs e)
{
this.InvalidateMeasure();
}
#endregion
#region TextWrapping (DependencyProperty)
/// <summary>
/// Gets or sets the TextWrapping property. This corresponds to TextBlock.TextWrapping.
/// </summary>
public TextWrapping TextWrapping
{
get { return (TextWrapping)GetValue(TextWrappingProperty); }
set { SetValue(TextWrappingProperty, value); }
}
public static readonly DependencyProperty TextWrappingProperty =
DependencyProperty.Register("TextWrapping", typeof(TextWrapping), typeof(DynamicTextBlock),
new PropertyMetadata(TextWrapping.NoWrap, new PropertyChangedCallback(OnTextWrappingChanged)));
private static void OnTextWrappingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DynamicTextBlock)d).OnTextWrappingChanged(e);
}
protected virtual void OnTextWrappingChanged(DependencyPropertyChangedEventArgs e)
{
this.textBlock.TextWrapping = (TextWrapping)e.NewValue;
this.InvalidateMeasure();
}
#endregion
#region LineHeight (DependencyProperty)
/// <summary>
/// Gets or sets the LineHeight property. This property corresponds to TextBlock.LineHeight;
/// </summary>
public double LineHeight
{
get { return (double)GetValue(LineHeightProperty); }
set { SetValue(LineHeightProperty, value); }
}
public static readonly DependencyProperty LineHeightProperty =
DependencyProperty.Register("LineHeight", typeof(double), typeof(DynamicTextBlock),
new PropertyMetadata(0.0, new PropertyChangedCallback(OnLineHeightChanged)));
private static void OnLineHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DynamicTextBlock)d).OnLineHeightChanged(e);
}
protected virtual void OnLineHeightChanged(DependencyPropertyChangedEventArgs e)
{
textBlock.LineHeight = LineHeight;
this.InvalidateMeasure();
}
#endregion
#region LineStackingStrategy (DependencyProperty)
/// <summary>
/// Gets or sets the LineStackingStrategy DependencyProperty. This corresponds to TextBlock.LineStackingStrategy.
/// </summary>
public LineStackingStrategy LineStackingStrategy
{
get { return (LineStackingStrategy)GetValue(LineStackingStrategyProperty); }
set { SetValue(LineStackingStrategyProperty, value); }
}
public static readonly DependencyProperty LineStackingStrategyProperty =
DependencyProperty.Register("LineStackingStrategy", typeof(LineStackingStrategy), typeof(DynamicTextBlock),
new PropertyMetadata(LineStackingStrategy.BlockLineHeight, new PropertyChangedCallback(OnLineStackingStrategyChanged)));
private static void OnLineStackingStrategyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
((DynamicTextBlock)d).OnLineStackingStrategyChanged(e);
}
protected virtual void OnLineStackingStrategyChanged(DependencyPropertyChangedEventArgs e)
{
this.textBlock.LineStackingStrategy = (LineStackingStrategy)e.NewValue;
this.InvalidateMeasure();
}
#endregion
/// <summary>
/// A TextBlock that gets set as the control's content and is ultiately the control
/// that displays our text
/// </summary>
private TextBlock textBlock;
/// <summary>
/// Initializes a new instance of the DynamicTextBlock class
/// </summary>
public DynamicTextBlock()
{
// create our textBlock and initialize
this.textBlock = new TextBlock();
this.Content = this.textBlock;
}
/// <summary>
/// Handles the measure part of the measure and arrange layout process. During this process
/// we measure the textBlock that we've created as content with increasingly smaller amounts
/// of text until we find text that fits.
/// </summary>
/// <param name="availableSize">the available size</param>
/// <returns>the base implementation of Measure</returns>
protected override Size MeasureOverride(Size availableSize)
{
// just to make the code easier to read
bool wrapping = this.TextWrapping == TextWrapping.Wrap;
Size unboundSize = wrapping ? new Size(availableSize.Width, double.PositiveInfinity) : new Size(double.PositiveInfinity, availableSize.Height);
string reducedText = this.Text;
// set the text and measure it to see if it fits without alteration
this.textBlock.Text = reducedText;
Size textSize = base.MeasureOverride(unboundSize);
while (wrapping ? textSize.Height > availableSize.Height : textSize.Width > availableSize.Width)
{
int prevLength = reducedText.Length;
reducedText = this.ReduceText(reducedText);
if (reducedText.Length == prevLength)
{
break;
}
this.textBlock.Text = reducedText + "...";
textSize = base.MeasureOverride(unboundSize);
}
return base.MeasureOverride(availableSize);
}
/// <summary>
/// Reduces the length of the text. Derived classes can override this to use different techniques
/// for reducing the text length.
/// </summary>
/// <param name="text">the original text</param>
/// <returns>the reduced length text</returns>
protected virtual string ReduceText(string text)
{
return text.Substring(0, text.Length - 1);
}
}
And in my XAML I use this like this:
<myNamespace:DynamicTextBlock Grid.Column="0" Text="Dies ist ein Text der sehr lang ist und umebrochen wird" HorizontalAlignment="Left" VerticalAlignment="Top" Margin="-1,0,0,0" Foreground="{Binding IsHighlighted, Converter={StaticResource BoolToBrushConverter}}" FontSize="32" />
Everything works cool. Now I want to apply a Style to this. But the following doesn´t work:
Style="{StaticResource PhoneTextLargeStyle}"
How the solution must be Extended to accept a StaticRessource as Style?
Styles in Windows Phone have a TargetType and they can only be set to controls of that type or its subtypes (child classes). PhoneTextLargeStyle's target is TextBlock, so you can't put it on other controls (like your DynamicTextBlock).
You would need to create a new style which targets DynamicTextBlock and sets the values that you like. I guess you can find out one way or another what the PhoneTextLargeStyle style does.

Applying same interaction trigger to multiple TextBoxes

I have multiple textboxes bound to different data, but I would like every one to launch the same command when the TextChanged event is fired. I could copy the interaction line under every textbox but I'm guessing there must be a way to use a template or style to get this working on all of them.
Here is the code for the first textbox
<TextBox Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="3" Height="28" HorizontalAlignment="Stretch" Margin="0,12,12,0" Name="TextBox_Description" VerticalAlignment="Top" TabIndex="3" Text="{Binding Item.Description, ValidatesOnDataErrors=True, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="TextChanged">
<i:InvokeCommandAction Command="{Binding DataChangedCommand}"/>
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
If your UpdateMode is always going to be PropertyChanged then there really isn't any reason not to listen to your own PropertyChangedEvent in your ViewModel. It will definitely improve testability.
private void OnPropertyChanged(object sender, PropertyChangedEventArgs args)
{
switch (args.PropertyName)
{
case "Prop1":
case "Prop2":
.
.
.
DataChangedCommand.Execute(null);
break;
}
}
I'm not positive how to do it with Interaction.Triggers, but in the past I've used the AttachedCommand dependency properties found here and simply used a Style
Normal XAML
<TextBox local:CommandBehavior.Event="TextChanged"
local:CommandBehavior.Command="{Binding DataChangedCommand}"/>
Using a Style
<Style TargetType="TextBox">
<Setter Property="local:CommandBehavior.Event" Value="TextChanged" />
<Setter Property="local:CommandBehavior.Command" Value="{Binding DataChangedCommand}" />
</Style>
EDIT: Since you can't download files at work, here's the code
CommandBehavior.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Markup;
using System.Windows;
using System.Windows.Input;
// Code to attach a command to any event
// From http://marlongrech.wordpress.com/2008/12/04/attachedcommandbehavior-aka-acb/
namespace Keys.Controls.Helpers.CommandHelpers
{
/// <summary>
/// Defines the attached properties to create a CommandBehaviorBinding
/// </summary>
public class CommandBehavior
{
#region Behavior
/// <summary>
/// Behavior Attached Dependency Property
/// </summary>
private static readonly DependencyProperty BehaviorProperty =
DependencyProperty.RegisterAttached("Behavior", typeof(CommandBehaviorBinding), typeof(CommandBehavior),
new FrameworkPropertyMetadata((CommandBehaviorBinding)null));
/// <summary>
/// Gets the Behavior property.
/// </summary>
private static CommandBehaviorBinding GetBehavior(DependencyObject d)
{
return (CommandBehaviorBinding)d.GetValue(BehaviorProperty);
}
/// <summary>
/// Sets the Behavior property.
/// </summary>
private static void SetBehavior(DependencyObject d, CommandBehaviorBinding value)
{
d.SetValue(BehaviorProperty, value);
}
#endregion
#region Command
/// <summary>
/// Command Attached Dependency Property
/// </summary>
public static readonly DependencyProperty CommandProperty =
DependencyProperty.RegisterAttached("Command", typeof(ICommand), typeof(CommandBehavior),
new FrameworkPropertyMetadata((ICommand)null,
new PropertyChangedCallback(OnCommandChanged)));
/// <summary>
/// Gets the Command property.
/// </summary>
public static ICommand GetCommand(DependencyObject d)
{
return (ICommand)d.GetValue(CommandProperty);
}
/// <summary>
/// Sets the Command property.
/// </summary>
public static void SetCommand(DependencyObject d, ICommand value)
{
d.SetValue(CommandProperty, value);
}
/// <summary>
/// Handles changes to the Command property.
/// </summary>
private static void OnCommandChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandBehaviorBinding binding = FetchOrCreateBinding(d);
binding.Command = (ICommand)e.NewValue;
}
#endregion
#region CommandParameter
/// <summary>
/// CommandParameter Attached Dependency Property
/// </summary>
public static readonly DependencyProperty CommandParameterProperty =
DependencyProperty.RegisterAttached("CommandParameter", typeof(object), typeof(CommandBehavior),
new FrameworkPropertyMetadata((object)null,
new PropertyChangedCallback(OnCommandParameterChanged)));
/// <summary>
/// Gets the CommandParameter property.
/// </summary>
public static object GetCommandParameter(DependencyObject d)
{
return (object)d.GetValue(CommandParameterProperty);
}
/// <summary>
/// Sets the CommandParameter property.
/// </summary>
public static void SetCommandParameter(DependencyObject d, object value)
{
d.SetValue(CommandParameterProperty, value);
}
/// <summary>
/// Handles changes to the CommandParameter property.
/// </summary>
private static void OnCommandParameterChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandBehaviorBinding binding = FetchOrCreateBinding(d);
binding.CommandParameter = e.NewValue;
}
#endregion
#region Event
/// <summary>
/// Event Attached Dependency Property
/// </summary>
public static readonly DependencyProperty EventProperty =
DependencyProperty.RegisterAttached("Event", typeof(string), typeof(CommandBehavior),
new FrameworkPropertyMetadata((string)String.Empty,
new PropertyChangedCallback(OnEventChanged)));
/// <summary>
/// Gets the Event property. This dependency property
/// indicates ....
/// </summary>
public static string GetEvent(DependencyObject d)
{
return (string)d.GetValue(EventProperty);
}
/// <summary>
/// Sets the Event property. This dependency property
/// indicates ....
/// </summary>
public static void SetEvent(DependencyObject d, string value)
{
d.SetValue(EventProperty, value);
}
/// <summary>
/// Handles changes to the Event property.
/// </summary>
private static void OnEventChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
CommandBehaviorBinding binding = FetchOrCreateBinding(d);
//check if the Event is set. If yes we need to rebind the Command to the new event and unregister the old one
if (binding.Event != null && binding.Owner != null)
binding.Dispose();
//bind the new event to the command if newValue isn't blank (e.g. switching tabs)
if (e.NewValue.ToString() != "")
binding.BindEvent(d, e.NewValue.ToString());
}
#endregion
#region Helpers
//tries to get a CommandBehaviorBinding from the element. Creates a new instance if there is not one attached
private static CommandBehaviorBinding FetchOrCreateBinding(DependencyObject d)
{
CommandBehaviorBinding binding = CommandBehavior.GetBehavior(d);
if (binding == null)
{
binding = new CommandBehaviorBinding();
CommandBehavior.SetBehavior(d, binding);
}
return binding;
}
#endregion
}
}
CommandBehaviorBinding.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Reflection;
using System.Windows;
// Code to attach a command to any event
// From http://marlongrech.wordpress.com/2008/12/04/attachedcommandbehavior-aka-acb/
namespace Keys.Controls.Helpers.CommandHelpers
{
/// <summary>
/// Defines the command behavior binding
/// </summary>
public class CommandBehaviorBinding : IDisposable
{
#region Properties
/// <summary>
/// Get the owner of the CommandBinding ex: a Button
/// This property can only be set from the BindEvent Method
/// </summary>
public DependencyObject Owner { get; private set; }
/// <summary>
/// The command to execute when the specified event is raised
/// </summary>
public ICommand Command { get; set; }
/// <summary>
/// Gets or sets a CommandParameter
/// </summary>
public object CommandParameter { get; set; }
/// <summary>
/// The event name to hook up to
/// This property can only be set from the BindEvent Method
/// </summary>
public string EventName { get; private set; }
/// <summary>
/// The event info of the event
/// </summary>
public EventInfo Event { get; private set; }
/// <summary>
/// Gets the EventHandler for the binding with the event
/// </summary>
public Delegate EventHandler { get; private set; }
#endregion
//Creates an EventHandler on runtime and registers that handler to the Event specified
public void BindEvent(DependencyObject owner, string eventName)
{
EventName = eventName;
Owner = owner;
Event = Owner.GetType().GetEvent(EventName, BindingFlags.Public | BindingFlags.Instance);
if (Event == null)
throw new InvalidOperationException(String.Format("Could not resolve event name {0}", EventName));
//Create an event handler for the event that will call the ExecuteCommand method
EventHandler = EventHandlerGenerator.CreateDelegate(
Event.EventHandlerType, typeof(CommandBehaviorBinding).GetMethod("ExecuteCommand", BindingFlags.Public | BindingFlags.Instance), this);
//Register the handler to the Event
Event.AddEventHandler(Owner, EventHandler);
}
/// <summary>
/// Executes the command
/// </summary>
public void ExecuteCommand()
{
if (Command != null && Command.CanExecute(CommandParameter))
Command.Execute(CommandParameter);
}
#region IDisposable Members
bool disposed = false;
/// <summary>
/// Unregisters the EventHandler from the Event
/// </summary>
public void Dispose()
{
if (!disposed)
{
Event.RemoveEventHandler(Owner, EventHandler);
disposed = true;
}
}
#endregion
}
}
EventHandlerGenerator.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection.Emit;
using System.Reflection;
// Code to attach a command to any event
// From http://marlongrech.wordpress.com/2008/12/04/attachedcommandbehavior-aka-acb/
namespace Keys.Controls.Helpers.CommandHelpers
{
/// <summary>
/// Generates delegates according to the specified signature on runtime
/// </summary>
public static class EventHandlerGenerator
{
/// <summary>
/// Generates a delegate with a matching signature of the supplied eventHandlerType
/// This method only supports Events that have a delegate of type void
/// </summary>
/// <param name="eventInfo">The delegate type to wrap. Note that this must always be a void delegate</param>
/// <param name="methodToInvoke">The method to invoke</param>
/// <param name="methodInvoker">The object where the method resides</param>
/// <returns>Returns a delegate with the same signature as eventHandlerType that calls the methodToInvoke inside</returns>
public static Delegate CreateDelegate(Type eventHandlerType, MethodInfo methodToInvoke, object methodInvoker)
{
//Get the eventHandlerType signature
var eventHandlerInfo = eventHandlerType.GetMethod("Invoke");
Type returnType = eventHandlerInfo.ReturnParameter.ParameterType;
if (returnType != typeof(void))
throw new ApplicationException("Delegate has a return type. This only supprts event handlers that are void");
ParameterInfo[] delegateParameters = eventHandlerInfo.GetParameters();
//Get the list of type of parameters. Please note that we do + 1 because we have to push the object where the method resides i.e methodInvoker parameter
Type[] hookupParameters = new Type[delegateParameters.Length + 1];
hookupParameters[0] = methodInvoker.GetType();
for (int i = 0; i < delegateParameters.Length; i++)
hookupParameters[i + 1] = delegateParameters[i].ParameterType;
DynamicMethod handler = new DynamicMethod("", null,
hookupParameters, typeof(EventHandlerGenerator));
ILGenerator eventIL = handler.GetILGenerator();
//load the parameters or everything will just BAM :)
LocalBuilder local = eventIL.DeclareLocal(typeof(object[]));
eventIL.Emit(OpCodes.Ldc_I4, delegateParameters.Length + 1);
eventIL.Emit(OpCodes.Newarr, typeof(object));
eventIL.Emit(OpCodes.Stloc, local);
//start from 1 because the first item is the instance. Load up all the arguments
for (int i = 1; i < delegateParameters.Length + 1; i++)
{
eventIL.Emit(OpCodes.Ldloc, local);
eventIL.Emit(OpCodes.Ldc_I4, i);
eventIL.Emit(OpCodes.Ldarg, i);
eventIL.Emit(OpCodes.Stelem_Ref);
}
eventIL.Emit(OpCodes.Ldloc, local);
//Load as first argument the instance of the object for the methodToInvoke i.e methodInvoker
eventIL.Emit(OpCodes.Ldarg_0);
//Now that we have it all set up call the actual method that we want to call for the binding
eventIL.EmitCall(OpCodes.Call, methodToInvoke, null);
eventIL.Emit(OpCodes.Pop);
eventIL.Emit(OpCodes.Ret);
//create a delegate from the dynamic method
return handler.CreateDelegate(eventHandlerType, methodInvoker);
}
}
}