Encryption

by MikeHogg31. May 2012 09:50

A really interesting project had me implementing encryption algorithms for a Point Of Sale vendor interface.  It was the closest thing I’ve done to ‘computer science’ and I was fascinated at manipulating integers that were one thousand digits long.  The vendor used a symmetric encryption wrapped in an asymmetric method, plus an additional byte manipulation algorithm, making it a few layers deep.  I used a proven Big Integer implementation, and some of the MS encryption libraries for certain steps of the algorithm, but a lot of it was byte level manipulation. 

In one of my favorite parts of the algorithm, I used a bit shift operator.  Never found a use for that in Business Intelligence!

private static byte[] ApplyOddParity(byte[] key)
        {
 for (var i = 0; i < key.Length; ++i)
            {
 int keyByte = key[i] & 0xFE; // 254? mask
                var parity = 0;
 for (var b = keyByte; b != 0; b >>= 1) parity ^= b & 1; // shift right until empty, setting parity  xor b bitand 1
                key[i] = (byte)(keyByte | (parity == 0 ? 1 : 0)); // set byte = byte bitor (unchange if match) 1 if not parity or 0 for odd
            }
 return key;
        }
public static string EncryptEAN(string eanhex, string decryptedmwkhex)
        {
 byte[] decryptedmwk = ConvertHexStringToByteArray(decryptedmwkhex);
 byte[] asciiean = Encoding.ASCII.GetBytes(eanhex.PadRight(8, ' '));
 
            TripleDESCryptoServiceProvider p = new TripleDESCryptoServiceProvider();
            p.Padding = PaddingMode.None;
            p.IV = new byte[8];
 // p.Mode = CipherMode.CBC; //  default 
 byte[] random = p.Key;// testing: random = FDCrypt.ConvertHexStringToByteArray("95:e4:d7:7c:6d:6c:6c") 
 byte checksum = GetCheckSum(asciiean);
 byte[] eanblock = new byte[16];
            Array.Copy(random, 0, eanblock, 0, 7);
            eanblock[7] = checksum;
            Array.Copy(asciiean, 0, eanblock, 8, 8);// BitConverter.ToString(eanblock)
            p.Key = decryptedmwk;
            ICryptoTransform e = p.CreateEncryptor();
 
 byte[] result = e.TransformFinalBlock(eanblock, 0, 16);
 return BitConverter.ToString(result, 0).Replace("-",String.Empty);
        }
public static string GetEncryptedMWK(string decryptedmwkhex, byte[] kek)
        {
 byte[] decryptedmwk = FDCrypt.ConvertHexStringToByteArray(decryptedmwkhex);
            TripleDESCryptoServiceProvider p = new TripleDESCryptoServiceProvider();
            p.Padding = PaddingMode.None;
            p.IV = new byte[8];
 // p.Mode = CipherMode.CBC; //  default 
 byte[] random = p.Key;//random = FDCrypt.ConvertHexStringToByteArray("e7:11:ea:ff:a0:ca:c3:ba")
            p.Key = decryptedmwk;// BitConverter.ToString(decryptedmwk)
            ICryptoTransform e = p.CreateEncryptor();
 byte[] checkvalue = e.TransformFinalBlock(new byte[8], 0, 8);// BitConverter.ToString(checkvalue) 
 byte[] keyblock = new byte[40];
            Array.Copy(random, keyblock, 8);
            Array.Copy(decryptedmwk, 0, keyblock, 8, 24);
            Array.Copy(checkvalue, 0, keyblock, 32, 8);// BitConverter.ToString(keyblock)
 
            p.Key = kek;
            e = p.CreateEncryptor();
 byte[] encryptedkeyblock = e.TransformFinalBlock(keyblock, 0, 40);
 string result = BitConverter.ToString(encryptedkeyblock,0, 40);
 return result.Replace("-",String.Empty); // should be 81 bytes inc null term?
        }

 

For testing, I built a UI in WPF.  Here you see how I wanted to encapsulate all the encryption stuff in a separate library (later to be used in a web site), yet needed a UI stub to go through the lengthy 18 step, two month long testing and certification process with the vendor.  I knew that UI could leverage my experience with the MVVM pattern in WPF to expose over 20 fields and half a dozen steps in fast iterations as we went through the vetting process, and the WPF UI became more of a helpful tool than a code maintenance drain like most UI’s. 

 

 

 

 

 

 

 

 

 

 


Tags:

WPF | C# | Encryption

Repository Patterns without Entity Framework

by MikeHogg2. March 2011 18:03

At the time EF came out, I was usually interfacing with million row Oracle databases, and EF didn’t connect to Oracle.  It wasn’t until I started working in a different industry later on that I got to enjoy EF.  So for some time my Repositories wrapped a Stored Procedure Data Layer. 

Here’s an example of my Repository usage in WPF using MVVM, LINQ, DI.  This was a financials reporting application and one of the interesting features that I was tasked with in this case was running calculations on the fly based on data from its different sources. 

First a couple simple models: The Group Model and a Details Model, both exposing some basic datasource properties as well as some derived properties and the methods to calculate them, and the Group with a List<> of Details:

 

public class MatchGroup : IEquatable<MatchGroup>// IEquatable for LINQ.Distinct()
    {
public decimal Match_Group_Id { get; set; }
public string Product { get; set; }
public DateTime Contract_Month { get; set; }
public decimal? Total_CER_Lots { get; set; }
public decimal? Total_GS_Lots { get; set; }
public decimal? Lot_Size { get; set; }
public decimal? CER_Price { get; set; }
public decimal? GS_Price { get; set; }
public decimal? CER_Float { get; set; }
public decimal? CER_Final_Float { get; set; }
public decimal? GS_Float { get; set; } 
        ... etc etc
public List<MatchDetail> Details { get; set; }
public string Single_ETI { get; set; }
public string Single_Goldman_Id { get; set; }
        #region Constructor
public MatchGroup()
        {
            Details = new List<MatchDetail>();
        }
        #endregion
public void Calculate()
        {
            SetSingleETI();
            CheckDetails();
            CheckGroup();
            SumGroupChecks();
        }
private void SetSingleETI()
        {
 if (Details.Count() > 1)
            {
                Single_ETI = "+";
            }
 else
            {
                Single_ETI = Details.Select(f => f.ETI).FirstOrDefault();
                Single_Goldman_Id = Details.Select(f => f.Goldman_Id).FirstOrDefault();
            }
        }
private void CheckDetails()
        {
 foreach (MatchDetail d in Details) d.CheckDetails();
        }
private void CheckGroup()
        {
            Float_Diff = GS_Price - CER_Float;
            Price_Diff = GS_Price - CER_Price;
            Qty_Diff = Total_GS_Lots - Total_CER_Lots;
        }
private void SumGroupChecks()
        {
 // most of these can be summed from linq grouping in initial creation? 
            CER_Settled_Total = Details.Sum(d => d.CER_Settled_Total);
            CER_Check = Details.Sum(d => d.CER_Check);
            CER_Diff = Details.Sum(d => d.CER_Diff);
 
        }
        #region IEquatable
public bool Equals(MatchGroup other)
        {
 return true ? Match_Group_Id == other.Match_Group_Id : false;
        }
public override int GetHashCode()
        {
 return Match_Group_Id == 0 ? 0 : Match_Group_Id.GetHashCode();
        }
        #endregion
    }

 

The Details Model was similar.

The ViewModel has some UI specific properties, but mostly just exposes the Model properties directly instantiated through constructor injection, as this is a read-only reporting View.

public class MatchGroupVM : ViewModelBase
    { 
        Models.MatchGroup _matchgroup;
public decimal Match_Group_Id { get { return _matchgroup.Match_Group_Id; } }
public string Product { get { return _matchgroup.Product; } }
public DateTime Contract_Month { get { return _matchgroup.Contract_Month; } }
public decimal? Total_CER_Lots { get{ return _matchgroup.Total_CER_Lots; } }
public decimal? Total_GS_Lots { get{ return _matchgroup.Total_GS_Lots; } }
        <snip>
public MatchGroupVM(Models.MatchGroup mg){
            _matchgroup = mg;
        }
}

I use a list of these GroupVMs on the main page or tab’s VM, with Repository injection.

public class FloatPricesVM : ViewModelBase
    {
        #region Fields
        #region Properties
public string SelectedProductName { get; set; }
public int SelectedDeliveryMonth { get; set; }
public int SelectedDeliveryYear { get; set; }
public List<string> ProductNames { get; set; }
public List<int> DeliveryMonths { get; set; }
public List<int> DeliveryYears { get; set; }
public List<MatchGroupVM> MatchGroups
        {
 get
            {
 return _matchgroups;
            }
 set
            {
                SetProperty(ref _matchgroups, value, "MatchGroups");
            }
        }
        #endregion
public FloatPricesVM(Models.MatchGroupRepository repo)
        {
            _repository = repo;
            _error = new ErrorVM();
            _dialog = new DialogVMBase();
            ProductNames = _repository.GetProductNames();
            DeliveryMonths = _repository.GetDeliveryMonths();
            DeliveryYears = _repository.GetDeliveryYears();
            SelectedDeliveryMonth = DateTime.Now.Month - 1;
            SelectedDeliveryYear = DateTime.Now.Year;
        }
        #region Commands
private List<MatchGroupVM> GetMatchGroups(string productname, DateTime deliverymonth)
        {
            List<MatchGroupVM> result = new List<MatchGroupVM>();
 try
            {
 foreach (Models.MatchGroup mg in _repository.GetMatchGroups(productname, deliverymonth))
                {
                    result.Add(new MatchGroupVM(mg));
                } 
            }
 catch (Exception e)
            {
                Error = new ErrorVM(e);
            }
 return result;
        }

The Repository wraps our data access layer, which is Oracle Stored Procs in this case.  It gets injected with a Pricing engine provider.

public class MatchGroupRepository
    {
        IFloatPriceProvider _fpp;
public MatchGroupRepository(IFloatPriceProvider fpp)
        {
            _fpp = fpp;
        }
public List<MatchGroup> GetMatchGroups(string productname, DateTime deliverymonth)
        {
            List<MatchGroup> result = new List<MatchGroup>();
            DataTable dt = new DataTable();
 try
            {
                dt = LIB.MyOraDBDAC.GetMatchDetails(productname, deliverymonth);
                var groups = from row in dt.AsEnumerable()
                             group row by new
                             {
                                 Id = (decimal)row.Field<OracleDecimal>("MATCH_GROUP_ID"),
                                 P = (string)row.Field<OracleString>("PRODUCT"),
                                 CM = (DateTime)row.Field<OracleDate>("CONTRACT_MONTH")
                             } into mg
                             orderby mg.Key.CM
                             select new Models.MatchGroup
                             {
                                 Match_Group_Id = mg.Key.Id,
                                 Product = mg.Key.P,
                                 Contract_Month = mg.Key.CM,
                                 Total_CER_Lots = RoundNullableDecimal(mg.Sum(f => OracleDecimalToDotNet(f.Field<OracleDecimal>("CER_LOTS"))), 0),
                                 Total_GS_Lots = RoundNullableDecimal(mg.Sum(f => OracleDecimalToDotNet(f.Field<OracleDecimal>("GS_LOTS"))), 0),
                                 Lot_Size = mg.Max(f => OracleDecimalToDotNet(f.Field<OracleDecimal>("LOT_SIZE"))),
                                 CER_Price = mg.Max(f => OracleDecimalToDotNet(f.Field<OracleDecimal>("CER_PRICE"))),
                                 GS_Price = mg.Max(f => OracleDecimalToDotNet(f.Field<OracleDecimal>("GS_PRICE"))),
 //CER_Float = _fpp.GetFloat(productname, mg.Key.CM),
                                 CER_Float = mg.Max(f => OracleDecimalToDotNet(f.Field<OracleDecimal>("CER_FLOAT"))),
 //CER_Final_Float = _fpp.GetFloat(productname, mg.Key.CM),
 
                             };
 foreach (Models.MatchGroup mg in groups)
                {
                    mg.Details = GetDetails(mg, dt);
                    mg.Calculate();
                    result.Add(mg);
                }
            }
 catch (InvalidCastException ex)
            {
 foreach (DataColumn c in dt.Columns)
                {
                    ex.Data.Add(c.ColumnName, c.DataType.Name);
                }
 throw;
            }
 return result;
        }
private List<Match.Models.MatchDetail> GetDetails(Models.MatchGroup mg, DataTable dt)
        {
            var dets = from row in dt.AsEnumerable()
                       where (decimal)row.Field<OracleDecimal>("MATCH_GROUP_ID") == mg.Match_Group_Id
                       select new Models.MatchDetail
                       {
                           MG = mg,
                           GS_Lots = RoundNullableDecimal(OracleDecimalToDotNet(row.Field<OracleDecimal>("GS_LOTS")), 2),
                           Goldman_Id = OracleStringToDotNet(row.Field<OracleString>("GOLDMAN_ID")),
                           ETI = OracleStringToDotNet(row.Field<OracleString>("ETI")),
                           CER_Lots = RoundNullableDecimal(OracleDecimalToDotNet(row.Field<OracleDecimal>("CER_LOTS")), 2),
                           CER_Settled_Value_TC = OracleDecimalToDotNet(row.Field<OracleDecimal>("CER_SETTLED_VALUE_TC")),
 
                       };
 return dets.ToList();
        }
private decimal? RoundNullableDecimal(decimal? d, int decimalplaces)
        {
 return d.HasValue ? (decimal?)Decimal.Round(d.Value, decimalplaces) : null;
        }
private decimal? OracleDecimalToDotNet(OracleDecimal od)
        {
            OracleDecimal d = OracleDecimal.SetPrecision(od, 28);
 return (d.IsNull) ? null : (decimal?)d;
        }
private string OracleStringToDotNet(OracleString os)
        {
 return (os.IsNull) ? null : (string)os;
        }
    }

 

There’s some neat stuff in there, catching InvalidCastException and adding info before re-throwing the error, Linq grouping, and here you see the call to the model’s Calculate function we showed in the beginning.

and that’s it.

Tags:

WPF | Linq

Hiding and Expanding Row Details in WPF

by MikeHogg18. November 2010 19:03

We had a normal datagrid, with lots of data.  Each of the rows was a parent to some list of other objects, and the users wanted to be able to expand a row to see the children detail rows. Nested Datagrids, a popular or usual feature request. The Toolkit DataGrid has a RowDetails object, but it is visible all the time.  Here’s how I set it up to have only one exclusive RowDetails visible at a time in WPF:

ViewModel has a property called IsSelected, and in addition to calling OnPropertyChanged event for itself, it calls OnPropertyChanged for another property, DetailsVisibility.  The Model Binder will do a Get on DetailsVisibility, and so we just need to put the logic there…

public class MatchGroupVM : ViewModelBase
    { 
        Models.MatchGroup _matchgroup;
bool _etiselected;
public decimal Match_Group_Id { get { return _matchgroup.Match_Group_Id; } }
public string Product { get { return _matchgroup.Product; } }
public DateTime Contract_Month { get { return _matchgroup.Contract_Month; } }
public decimal? Lot_Size { get{ return _matchgroup.Lot_Size; } }
        <snip>
public decimal? Price_Diff { get { return _matchgroup.Price_Diff; } }
public decimal? Float_Diff { get { return _matchgroup.Float_Diff; } }
public List<Models.MatchDetail> Details { get{ return _matchgroup.Details; } }
public string Single_ETI { get{ return _matchgroup.Single_ETI; } }
public string Single_Goldman_Id { get{ return _matchgroup.Single_Goldman_Id; } }
public bool ETISelected
        {
 get
            {
 return _etiselected;
            }
 set
            {
 if (_etiselected != value)
                {
                    _etiselected = value;
                    OnPropertyChanged("ETISelected");
                    OnPropertyChanged("DetailsVisibility");
                }
            }
        }
public System.Windows.Visibility DetailsVisibility
        {
 get
            {
 return Details.Count > 1 == true && ETISelected == true ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
            }
 set { var bindingdummysetter = value; }
        } 
public MatchGroupVM(Models.MatchGroup mg){
            _matchgroup = mg;
        }
    }

 

Note in this case we are not writing our own Getters and Setters in our VM, but simply exposing the composition model.  That is because this VM is a readonly report.  Similarly, the Model Binder doesn’t ever need to Set DetailsVisibility, but by calling OnPropertyChanged the Getter is called, so this one line handles both Showing a Details grid and Hiding a Details Grid.  A parent VM handles the populating of this VM and its Details.

The View will have the nested grid in the RowDetailsTemplate, just like any other, but it binds its DetailsVisibility to the Model.  Note that the nested DetailsVisibility needs to be set to Collapsed to fix the cascading style of WPF.  And we add some eye candy to a row’s colors when it becomes selected. 

<tk:DataGrid Name="GridFloatPrices" ItemsSource="{Binding MatchGroups}"Grid.Row="1"
 RowHeaderWidth="1" IsReadOnly="True" AutoGenerateColumns="False"
 AlternatingRowBackground="Honeydew" CanUserAddRows="False"
 SelectionUnit="Cell" ScrollViewer.CanContentScroll="False"
 >
 
 <tk:DataGrid.Resources>
 <StyleTargetType="{x:Type tk:DataGridCell}" >
 <Style.Triggers>
 <Trigger Property="IsSelected" Value="True">
 <Setter Property="Background" Value="LightSkyBlue"/>
 <Setter Property="Foreground" Value="Black"/>
 <Setter Property="BorderBrush" Value="LightSkyBlue"/>
 </Trigger>
 </Style.Triggers>
 </Style>
 <Style TargetType="tk:DataGridRow">
 <Setter Property="DetailsVisibility" Value="{Binding DetailsVisibility}" /> 
 </Style>
 <Style TargetType="{x:Type tkp:DataGridColumnHeader}">
 <Setter Property="ContentTemplate">
 <Setter.Value>
 <DataTemplate>
 <TextBlock TextWrapping="Wrap" Text="{Binding}"></TextBlock>
 </DataTemplate>
 </Setter.Value>
 </Setter>
 </Style>
 </tk:DataGrid.Resources>
 
 <tk:DataGrid.RowDetailsTemplate>
 <DataTemplate>
 <Grid Background="LightGray" HorizontalAlignment="Stretch" ScrollViewer.CanContentScroll="False">
 <tk:DataGrid ItemsSource="{Binding Details}" RowBackground="LightYellow"
 Margin="749,0,0,0" IsReadOnly="True" CanUserAddRows="False"
 HeadersVisibility="None" AutoGenerateColumns="False" ScrollViewer.ScrollChanged="OnMouseWheel"
 Width="1450" ScrollViewer.HorizontalScrollBarVisibility="Hidden"
 HorizontalAlignment="Left">
 <tk:DataGrid.Resources>
 <Style TargetType="tk:DataGridRow">
 <Setter Property="DetailsVisibility" Value="Collapsed"/><!-- to turn off cascading style from master grid -->
 </Style>
 </tk:DataGrid.Resources>
 
 <tk:DataGrid.Columns>
                               <snip>
 <tk:DataGridTextColumn Binding="{Binding CER_Check}" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding CER_Diff}" Width="100"></tk:DataGridTextColumn>
 
 <tk:DataGridTextColumn Binding="{Binding GS_Settled_Value}" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding MV_Diff_Value}" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding GS_Settled_Total}" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding GS_Check}" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding GS_Diff}" Width="100"></tk:DataGridTextColumn>
 
 <tk:DataGridTextColumn Binding="{Binding Settled_Diff}" Width="100"></tk:DataGridTextColumn>
 </tk:DataGrid.Columns>
 </tk:DataGrid>
 </Grid>
 </DataTemplate>
 </tk:DataGrid.RowDetailsTemplate>
 <tk:DataGrid.Columns>
 <tk:DataGridTextColumn Binding="{Binding Product}" Header="Product" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding Contract_Month, StringFormat=MMM-yyyy}" Header="Contract Month" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding Match_Group_Id}" Header="Match Group Id" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding CER_Price}" Header="CER Price" Width="75"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding Lot_Size}" Header="Lot Size" Width="75"></tk:DataGridTextColumn>
 
 <tk:DataGridTextColumn Binding="{Binding Single_ETI}" Header="ETI" Width="100">
 <tk:DataGridTextColumn.CellStyle>
 <Style TargetType="tk:DataGridCell">
 <Setter Property="IsSelected" Value="{Binding ETISelected, Mode=OneWayToSource}"/>
 <Style.Triggers>
 <Trigger Property="IsSelected" Value="True">
 <Setter Property="Background" Value="LightSkyBlue"/>
 <Setter Property="Foreground" Value="Black"/>
 <Setter Property="BorderBrush" Value="LightSkyBlue"/>
 </Trigger>
 </Style.Triggers>
 </Style>
 </tk:DataGridTextColumn.CellStyle>
 </tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding Single_Goldman_Id}" Header="Goldman Id" Width="100"></tk:DataGridTextColumn> 
 <tk:DataGridTextColumn Binding="{Binding Total_CER_Lots}" Header="Tot CER Lots" Width="75"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding Total_GS_Lots}" Header="Tot GS Lots" Width="75"></tk:DataGridTextColumn>
                <snip>               
 <tk:DataGridTextColumn Binding="{Binding Settled_Diff}" Header="Settled Diff" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding Qty_Diff}" Header="Qty Diff" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding Price_Diff}" Header="Price Diff" Width="100"></tk:DataGridTextColumn>
 <tk:DataGridTextColumn Binding="{Binding Float_Diff}" Header="Float Diff" Width="100"></tk:DataGridTextColumn>
 </tk:DataGrid.Columns>
 </tk:DataGrid> 
and that’s it.

Tags:

WPF

WPF Dialogs

by MikeHogg15. July 2010 21:13

Here’s the modal dialog I used in my WPF projects, where a windowless popup obstructs the application and the app is dimmed and disabled in the background until the user responds...

The view root element uses a grid sized to the entire parent window and only slightly opaque (much like I’ve seen this done in web pages) and the actual dialog is a smaller grid inside that.

 

<DataTemplate x:Key="Dialog" DataType="DialogVM">
<Grid Height="{Binding Height, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}"
 Width="{Binding Width, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Window}}}">
 <Grid.Background>
 <SolidColorBrush Color="LightGray" Opacity=".6"></SolidColorBrush>
 </Grid.Background>
 <Grid.ColumnDefinitions>
 <ColumnDefinition Width="50"></ColumnDefinition>
 <ColumnDefinition Width="*"></ColumnDefinition>
 <ColumnDefinition Width="50"></ColumnDefinition>
 </Grid.ColumnDefinitions>
 <Grid.RowDefinitions>
 <RowDefinition Height="100"></RowDefinition>
 <RowDefinition Height="*"></RowDefinition>
 </Grid.RowDefinitions>
 <Border HorizontalAlignment="Center" BorderBrush="Black" BorderThickness="1"
 Background="LightSkyBlue" Height="120" Width="320"
 Grid.Column="1" Grid.Row="1" CornerRadius="10" >
 <Border.Effect>
 <DropShadowEffect ShadowDepth="10"/>
 </Border.Effect>
 <Grid>
 <Grid.RowDefinitions>
 <RowDefinition Height="20"></RowDefinition>
 <RowDefinition Height="100"></RowDefinition>
 </Grid.RowDefinitions>
 <Border BorderThickness="0,0,0,1" Grid.Row="0"><!-- BorderBrush="Black"--><!-- not using border looks too much like a window the user wants to drag it -->
 <TextBlock Text="{Binding Title}" Margin="10,3,0,0"></TextBlock>
 </Border>
 <Grid Grid.Row="1" >
 <Grid.RowDefinitions>
 <RowDefinition Height="60"></RowDefinition>
 <RowDefinition Height="40"></RowDefinition>
 </Grid.RowDefinitions>
 <Grid Background="AliceBlue"Grid.Row="0" >
 <TextBlock Text="{Binding Message}" FontSize="14" TextWrapping="Wrap"
 Margin="10" TextAlignment="Center" ></TextBlock>
 </Grid>
 <Border Background="AliceBlue" Grid.Row="1">
 <Border.CornerRadius>
 <CornerRadius BottomLeft="10" BottomRight="10"></CornerRadius>
 </Border.CornerRadius>
 <Button Content="OK" Command="{Binding CmdOK}" Width="50" Height="20" ></Button>
 </Border>
 </Grid>
 </Grid>
 </Border>
</Grid>
</DataTemplate>

 

I use one for messages, and an extended one for Error Messages.  I put these anywhere at the bottom of one of my WPF windows, like this

 

 <ContentControl Content="{Binding Dialog}" ContentTemplate="{StaticResource Dialog}"
 Grid.RowSpan="2" Visibility="{Binding Dialog.Visibility}">
 </ContentControl>
 <ContentControl Content="{Binding Error}" ContentTemplate="{StaticResource Error}"
 Grid.RowSpan="2" Visibility="{Binding Error.Visibility}">
 </ContentControl>
</Grid>
</DataTemplate>
</ResourceDictionary>

 

And add the class as a Property to the window’s ViewModel:

 

public class SomeViewModel
    {
        #region Fields
        ...
        DialogVMBase _dialog;
        ErrorVM _error;
        #endregion
        #region Properties
        ...
public DialogVMBase Dialog
        {
 get
            {
 return _dialog;
            }
 set
            {
 this._dialog = value;
 this._dialog.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_dialog_PropertyChanged);
 this.OnPropertyChanged("Dialog"); // this one sends notice to Binding to open the dialog
            }
        }
public ErrorVM Error
        {
 get
            {
 return _error;
            }
 set
            {
 this._error = value;
 this._error.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(_error_PropertyChanged);
 this.OnPropertyChanged("Error");
            }
        }
// simple button OK clicks change Visibility property and we catch dialogVM.PropertyChanged here
void _dialog_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
 this.OnPropertyChanged("Dialog");// this one sends notice to ListVM Binding (to close dialog)
        }
void _error_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
 this.OnPropertyChanged("Error");
        }
        #endregion

You see there we attach a delegate in the parent window to PropertyChanged on the Dialog, so when a property changes in the Dialog, the parent is notified.  In the Dialog next you will see clicking OK just sets the Visibility to Hidden and calls PropertyChanged, which will then call the parent window’s event handler, whose Model Binder notices the Visibility property is changed, and then registers the Hide in the UI.

public class DialogVMBase : ViewModelBase
    {
        #region fields
string _title;
string _message;
        System.Windows.Visibility _visibility;
        RelayCommand _cmdok;
        #endregion
// derived classes must set Visible explicitly in their own constructor
public DialogVMBase() {
 this._visibility = System.Windows.Visibility.Hidden;
        } 
// this contructor gets Visible by default
public DialogVMBase(string title, string message) 
        { 
 this.Message = message;
 this.Title = title;
        }
        #region Properties
public virtual string Title
        {
 get { return _title; }
 set
            {
                SetProperty(ref _title, value, "Title");
            }
        }
public virtual string Message
        {
 get { return _message; }
 set
            {
                SetProperty(ref _message, value, "Message");
            }
        }
public virtual System.Windows.Visibility Visibility
        {
 get { return _visibility; }
 set
            {
 if (_visibility == value) return;
                _visibility = value;
 this.OnPropertyChanged("Visibility");
            }
        }
        #endregion
public event EventHandler ButtonOkClicked;
        #region Commands
public virtual RelayCommand CmdOK
        {
 get
            {
 if (_cmdok == null)
                {
                    _cmdok = new RelayCommand(
                        param => this.OK(),
                        param => this.OKEnabled()
                        );
                }
 return _cmdok;
            }
        }
public virtual bool OKEnabled()
        {
 return true;
        }
public virtual void OK()
        {
            EventHandler handler = ButtonOkClicked;
 if (handler != null) handler(this, null);
 this.Visibility = System.Windows.Visibility.Hidden;
        }
        #endregion
    }

In my ErrorVM I add some more exception properties

public class ErrorVM : DialogVMBase
    {
        #region fields
        Exception _exception;
string _stack;
        Exception _inner;
        Dictionary<object,object> _data;
        #endregion
public ErrorVM() { }
public ErrorVM(Exception e)
        {
 if (e != null)
            {
                _exception = e;
                this.Title = "Error";
                this.Message = e.Message;
                _stack = e.StackTrace;
                _inner = e.InnerException;
                _data = new Dictionary<object, object>();
                // on rethrow the first exception sometimes becomes an inner
 while (e.Data.Count == 0 && e.InnerException != null)
                {
                    e = e.InnerException;
                }
                foreach (System.Collections.DictionaryEntry d in e.Data)
                    _data.Add(d.Key, d.Value);
                this.Visibility = System.Windows.Visibility.Visible;
            }
        }
        #region Properties
public string StackTrace
        {
 get { return _stack; }
 set
            {
 if (_stack == value) return;
                _stack = value;
                this.OnPropertyChanged("StackTrace");
            }
        }
public Exception InnerException
        {
 get { return _inner; }
 set
            {
 if (_inner == value) return;
                _inner = value;
                this.OnPropertyChanged("InnerException");
            }
        }
public Dictionary<object,object> Data
        {
 get { return _data; }
 set
            {
 if (_data == value) return;
                _data = value;
                this.OnPropertyChanged("Data");
            }
        }
        #endregion
    }

and then the parent can call it at any time, like this:

 if (MatchGroups.Count == 0) Dialog = new DialogVMBase("No Results",
 String.Format("No Results found for your Product: {0}", SelectedProductName));

and that’s it.

Tags:

WPF

About Mike Hogg

Mike Hogg is a c# developer in Brooklyn.

More Here

Favorite Books

This book had the most influence on my coding style. It drastically changed the way I write code and turned me on to test driven development even if I don't always use it. It made me write clearer, functional-style code using more principles such as DRY, encapsulation, single responsibility, and more.amazon.com

This book opened my eyes to a methodical and systematic approach to upgrading legacy codebases step by step. Incrementally transforming code blocks into testable code before making improvements. amazon.com

More Here