Showing posts with label .net 3.5 sp1. Show all posts
Showing posts with label .net 3.5 sp1. Show all posts

How to redirect Console and Debug output to your UI

How many times have you been in your test environment trying to understand whats happening with no debugger to call on.
I find this especially frustrating in WPF where binding errors fail silently. Log files are a drag - wouldn't it be cool if you could pipe the console/debug output to somewhere in your UI?

It turns out this is pretty easy. Console output can be redirected to a stream

You can easily subclass TextWriter to pipe the stream output to a textbox for example

            // Instantiate the writer 
            TextWriter _writer = new TextBoxStreamWriter(txtMessage); 
            // Redirect the out Console stream 
            Console.SetOut(_writer); 
            Console.WriteLine("Now redirecting console output to the text box");


    public class TextBoxStreamWriter : TextWriter
    {
        TextBox _output = null;

        public TextBoxStreamWriter(TextBox output)
        {
            _output = output;
        }

        public override void Write(char value)
        {
            base.Write(value);
            _output.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        _output.AppendText(value.ToString());
                    })
            ); // When character data is written, append it to the text box. 
        }

        public override Encoding Encoding
        {
            get { return System.Text.Encoding.UTF8; }
        }
    }

Undoing what you've done.
            StreamWriter standardOutput = new StreamWriter(Console.OpenStandardOutput());
            standardOutput.AutoFlush = true;
            Console.SetOut(standardOutput);

Redirecting debug output is even easier as it offers a lot of flexibility via tracelisteners out of the box

In this example I’m leverging the code from above to send debug output to the console and hence to my textbox

            Debug.Listeners.Add(new TextWriterTraceListener(Console.Out));
            Debug.WriteLine("Now redirecting debug output to the text box");

Why doesn't my keyboard navigation work on templated ComboBoxes/ItemControls

There is a little gotcha here when you overwrite the itemcontainerstyle on an items control. If you want to allow quick access items by typing prefixes of strings you need to setup some attached properties.

There is 2 ways you can go you can either add the TextSearch.TextPath attached property on the ItemControl or add the TextSearch.Text attached property on the individual item.

Example:

<ComboBox IsEditable="true" TextSearch.TextPath="Name">
            <Image Name="Cat" Source="data\cat.png"/>
            <Image Name="Dog" Source="data\dog.png"/>
            <Image Name="Fish" Source="data\fish.png"/>
ComboBox>

<ComboBox IsEditable="true">
<Image TextSearch.Text="Cat" Source="data\cat.png"/>
            <Image TextSearch.Text="Dog" Source="data\dog.png"/>
            <Image TextSearch.Text="Fish" Source="data\fish.png"/>
ComboBox>


Example of how to do this when overwriting the itemcontainerstyle:

        <ComboBox.ItemContainerStyle>
            <Style TargetType="{x:Type ComboBoxItem}">
                <Setter Property="TextSearch.Text">
                    <Setter.Value>
                        <MultiBinding StringFormat="{} {0} {1}">
                            <Binding Path="FirstName"/>
                            <Binding Path="LastName"/>
                        MultiBinding>
                    Setter.Value>
                Setter>
            Style>
        ComboBox.ItemContainerStyle>
  


How to iterate over the rows and cells in an Xceed WPF DataGrid

I found this handy code on the Xceed forums today. See http://xceed.com/CS/forums/permalink/26496/26518/ShowThread.aspx#26518

            foreach (object item in thisDataGrid1.Items)
            {
                Dispatcher.BeginInvoke(new Action<object>(DoRow), DispatcherPriority.ApplicationIdle, item);
            }

        private void DoRow(object item)
        {
            Xceed.Wpf.DataGrid.DataRow row = this.DataGrid1.GetContainerFromItem(item) as Xceed.Wpf.DataGrid.DataRow;
            if (row != null)
            {
                foreach (Xceed.Wpf.DataGrid.DataCell c in row.Cells)
                {
                    if (c != null)
                    {
                        //Do something to the cell
                    }
                }
            }
        }

How do you use a converter on a trigger


You can’t.

You can however use a data trigger and set the binding RelativeSource to Self. Data Triggers allow binding and bindings lets you have converters. Yey

Example:

       <Button Content="I change colour depending on my width for some reason">
            <Button.Triggers>
                <DataTrigger
                    Binding="{Binding
                    Path=Width,
                    RelativeSource={RelativeSource Self},
                    Converter={StaticResource isLessThanConverter},
                    ConverterParameter=50}"
                    Value="True">
                    <Setter Property="Button.Background" Value="Red" />
                DataTrigger>
            Button.Triggers>
        Button>

How to make Tooltips display on disabled WPF controls


This is old news but I find lots of people still don't know about this.

By default, WPF does not show tooltips on controls that have been disabled. To fix this, set the attached property ToolTipService.ShowOnDisabled.

E.g.
<Button Content="Example Button" 
 ToolTipService.ShowOnDisabled="True"  
 ToolTip="I’m visible even when the control is not enabled"/>

How to obtain access to nested types through XAML

Answer: "+" indicates nested type in XAML

For example I want to access the View member on the Shipping type which is nested inside the Setup type e.g.

public struct Setup
{
  public struct Shipping
  {
    public const string View = "This is the value I want to access";
  }
}

The syntax would be CommandParameter="{x:Static demoAlias:Setup+Shipping.View}"

PropertyChangedEventManager is not marked as serializable

If you ever get the error “PropertyChangedEventManager is not marked as serializable.” This is probably because you are implementing INotifiyPropertyChanged incorrectly.

The solution is to mark your PropertyChangedEventManager as NonSerialized.

#region INotifyPropertyChanged Members

[field: NonSerialized]
public event PropertyChangedEventHandler PropertyChanged;


protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null)
{
handler(this, e);
}
}

#endregion

Binding to a Settings file

Something that I find really cool is that you can bind directly to a user settings file in wpf. In the example below I've bound a toggle button to a user setting in a settings file. Toggling the button will update the settings file with 0 code!

<ToggleButton x:Name="btnNetworkSpeed" Margin="5,0,0,0" FlowDirection="LeftToRight" IsChecked="{Binding Source={x:Static InfrastructureProperties:Settings.Default}, Path=IsSlowConnectionSpeed, Mode=TwoWay}" FontSize="9"/>

The trick to making this work is to change the settings file access modifier to Public. To do this go into the settings file and at the top of the designer there is a drop down. What this does under the covers is change the compile tool from 'SettingsSingleFileGenerator' to 'PublicSettingsSingleFileGenerator'

If you want the setting to be persisted on the closing event of the application add this line and you're done.

protected override void OnExit(ExitEventArgs e)
{
Modules.Infrastructure.Properties.Settings.Default.Save();
...
}

Bug in ItemsControl with nested bindings

I came across a weird bug in the wpf ItemsControl today. I found that when I added a converter to the the ItemTemplate it killed the binding on the ItemTemplate.

After a lot of searching I found a few other people with the same problem but not many solutions. One workaround that seems to have been successful is to move the template into a resource. This didn't work for me but might be worth a try if you come across the same thing.

Workaround for bug in WPF Splash Screen

In WPF 3.5 SP1 Microsoft added a basic splash screen. Unfortunately there is a known bug that throws an error like this if you take focus away from the splash screen:

System.ComponentModel.Win32Exception was unhandled

Message="The operation completed successfully"

Source="WindowsBase"

ErrorCode=-2147467259

NativeErrorCode=0

StackTrace:

at MS.Win32.UnsafeNativeMethods.SetActiveWindow(HandleRef hWnd)

at System.Windows.SplashScreen.Close(TimeSpan fadeoutDuration)

at System.Windows.SplashScreen.b__0(Object splashObj)

at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)

at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)

at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)

at System.Windows.Threading.DispatcherOperation.InvokeImpl()

at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)

at System.Threading.ExecutionContext.runTryCode(Object userData)

at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)

at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)

at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

at System.Windows.Threading.DispatcherOperation.Invoke()

at System.Windows.Threading.Dispatcher.ProcessQueue()

at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)

at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)

at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)

at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Boolean isSingleParameter)

at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)

at System.Windows.Threading.Dispatcher.WrappedInvoke(Delegate callback, Object args, Boolean isSingleParameter, Delegate catchHandler)

at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Boolean isSingleParameter)

at System.Windows.Threading.Dispatcher.Invoke(DispatcherPriority priority, Delegate method, Object arg)

at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)

at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)

at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)

at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)

at System.Windows.Threading.Dispatcher.Run()

at System.Windows.Application.RunDispatcher(Object ignore)

at System.Windows.Application.RunInternal(Window window)

at System.Windows.Application.Run(Window window)

at System.Windows.Application.Run()

at Qmastor.WinApp.PortManagement.Loader.App.Main() in C:\SourceCode\PitToPort\PortManagement\Qmastor.WinApp.PortManagement.Loader\obj\Debug\App.g.cs:line 0

at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args)

at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)

at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()

at System.Threading.ThreadHelper.ThreadStart_Context(Object state)

at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)

at System.Threading.ThreadHelper.ThreadStart()

InnerException:

The suggested solution on the connect website doesn't seem to work for me and I failed to find any other solutions on the net other than rolling your own splash screen. However I found that adding this line of code before closing the splash screen seems to fix the problem for me:

App.Current.MainWindow.Focus();

_splash.Close(TimeSpan.Zero);