Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Building a WPF Sudoku Game: Part 5 - The AI Battle: Loading and Comparing AI Plug-ins

  Building Sudoku using Windows Presentation Foundation and XAML, Microsoft's new declarative programming language. This is the 5th article from a series of 5 articles and focusses on loading and comparing AI Plug-ins.

Difficulty: Easy
Time Required: 1-3 hours
Cost: Free
Software: Visual C# 2005 Express Edition .NET Framework 3.0 Runtime Components Windows Vista RTM SDK Visual Studio Extensions for the .NET Framework 3.0 November 2006 CTP
Hardware:
Download: Download (note: Tasos Valsamidis has an updated version that supports Expression Blend here)

Note: This article has been updated to work and compile with the RTM version of the Windows SDK. 

Welcome to the fifth and final part of my Windows Presentation Foundation tutorial! In this tutorial we'll be wrapping up our Sudoku game by adding support for comparing multiple plug-ins, multiple threads, new notification messages, and a cool databound graph control. First, let's take a look at the new interface we are trying to build:

On the left, there is a list of all the installed plug-ins, in the middle, our graphing control, and on the right the details for the current plug-in. To get this working we first need a way to enumerate all the plug-ins available to the app. The simplest way of doing this is to dump all the .dll files into a directory, which I've called “Solvers”. It's pretty simple to get a list of the assemblies in the folder, using the directory class:

 

string[] plugins = Directory.GetFiles("Solvers\\", "*.dll");
foreach (string p in plugins)
{
LoadSolvers(p);
}

I've also added a new field in the Window1 class to hold the list of loaded plug-ins, which eventually ends up as the ItemsSource for the listbox:

 

ObservableCollection<ISudokuSolver> Solvers = 
new ObservableCollection<ISudokuSolver>();

We also need some other groundwork code to accept the new plug-in folder. First, we need to define that our application can load assemblies from a folder other than its base (where the .exe is) and the system folders. We do this by defining an app.config. Select the SudokuFX project, right click and select “Add new item…” then “Application Configuration File”. This file needs to be modified to specify the subdirectory as a valid assembly location:

 

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <probing privatePath="Solvers"/>
    </assemblyBinding>
  </runtime>
</configuration>

On top of that, we also need to alter our new app domain, in a similar fashion:

 

AppDomainSetup ads = new AppDomainSetup();
ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
ads.PrivateBinPath = System.IO.Path.GetDirectoryName(System.IO.Path.GetFullPath(path));
PermissionSet ps = new PermissionSet(null);
ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
SolverDomain = AppDomain.CreateDomain("New AD", null, ads, ps);

Then we need to populate our list with the proxy object we create:

 

Type[] ts = asm.GetTypes();
foreach (Type t in ts)
{
if (Array.IndexOf(t.GetInterfaces(), typeof(ISudokuSolver)) != -1)
{
Type container = typeof(SudokuSolverContainer);
SudokuSolverContainer ssc = SolverDomain.CreateInstanceAndUnwrap(
container.Assembly.FullName, container.FullName)
as SudokuSolverContainer;
ssc.Init(t);
Solvers.Add(ssc);
}
}

Finally, a simple datatemplate needs to be added to the listbox to display the Name property of the object, but you should be a pro at that by now. I also added a custom template to give the list items checkboxes, but that's purely cosmetic and isn't strictly required. Next, we can bind the right panel's datacontext to the selected item in the left listbox, this way as the selection changes the info will update:

 

<StackPanel x:Name="InfoPanel" 
DataContext="{Binding ElementName=SolverList, Path=SelectedItem}"> <TextBlock Foreground="Black" Text="Solver Info:" FontWeight="Bold" FontSize="12"/> <TextBlock FontSize="8" Text=" "/> <TextBlock Foreground="Black" Text="{Binding Path=Name}"/> <TextBlock Foreground="Black" Text="{Binding Path=Author}"/> <TextBlock FontSize="8" Text=" "/> <TextBlock Foreground="Black" Text="{Binding Path=Description}"
HorizontalAlignment="Stretch" TextWrapping="WrapWithOverflow"/> </StackPanel>

Now that that's working, lets start building the graph control. Add a new user control to the project, but this time alter it to derive from ListBox. Wait? That control's just a listbox? Yup, and not only that, you can throw almost any type of object in it. How does it know the height of the bars then? The height of each bar is relative to the other bars since the height of the control itself is finite. The answer is attached properties. Using attached properties you can dynamically add new properties to an object at runtime, the only requirement is that the object derives from DependencyObject, which in WPF means almost any object will work. In fact you've already used attached properties, DockPanel.Dock is an example of one. Each control in a DockPanel needs to remember where it's docked but adding a property to each class would be redundant because being in a dockpanel is a special case. This way, we can use a property of DockPanel, but store a value on each object. Defining an attached property is very similar to declaring a dependency property:

 

public static readonly DependencyProperty BarHeightProperty = 
DependencyProperty.RegisterAttached("BarHeight", typeof(double),
typeof(DependencyObject), new PropertyMetadata(0.0));

but, instead of defining a property, we need to define static access methods:

 

public static double GetBarHeight(DependencyObject d)
{
return (double)d.GetValue(BarHeightProperty);
}

public static void SetBarHeight(DependencyObject d, double h)
{
d.SetValue(BarHeightProperty, h);
}

 

Now, we need to define the class we're going to store in the list, this simple class just holds a reference to the solver used, how long it took to run, and whether it was successful in solving the sudoku:

 

public class SolverResult : DependencyObject
{
TimeSpan timeTaken;

public TimeSpan TimeTaken
{
get
{
return timeTaken;
}
set
{
timeTaken = value;
}
}

ISudokuSolver solver;

public ISudokuSolver Solver
{
get
{
return solver;
}
set
{
solver = value;
}
}

bool failed;

public bool Failed
{
get
{
return failed;
}
set
{
failed = value;
}
}
}

Next, we need to add some code to figure out the heights of the bars in the graph, essentially I just find the highest bar and assume that it fills the control vertically, and then set the properties on each item. This only needs to be done when the contents change so conveniently we can override the event handler:

 

protected override void OnItemsChanged(
System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
base.OnItemsChanged(e);
long maxVal = 0;
foreach (SolverResult s in Items)
{
if (!s.Failed && s.TimeTaken.Ticks > maxVal) maxVal = s.TimeTaken.Ticks;
}

foreach (SolverResult s in Items)
{
if (s.Failed)
{
GraphControl.SetBarHeight(s, ActualHeight - 25);
}
else { double h = (double)s.TimeTaken.Ticks /
(double)maxVal * (ActualHeight - 25);
if (h > 10)
{
GraphControl.SetBarHeight(s, h);
}
else { GraphControl.SetBarHeight(s, 10); } } GraphControl.SetIndex(s, Items.IndexOf(s)); } }

The index property is another attached property I've defined; you'll see where it's used in just a bit. For now though, it's all about the data templates:

 

<DataTemplate x:Key="BarTemplate">
  <Grid x:Name="Bar" Margin="3,3,3,0" Width="50" VerticalAlignment="Bottom"
    Height="{Binding Path=BarHeight}" Background="Red" >
    <TextBlock x:Name="BarText" VerticalAlignment="Center"
        Foreground="White" HorizontalAlignment="Center" 
Text="{Binding Path=TimeTaken}"> <TextBlock.LayoutTransform> <RotateTransform Angle="90"/> </TextBlock.LayoutTransform> </TextBlock> </Grid> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Path=Failed}" Value="True"> <Setter TargetName="Bar" Property="Opacity" Value="0.5"/> <Setter TargetName="BarText" Property="Text" Value="Failed"/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate>

Here we make containers to hold the bars and set their heights based on our new property. I also added some triggers to ghost out the bar if the solver failed to solve the sudoku grid.

As you can see, it works but yuck, what is this, 16-color Windows 3.1? We've got to make this look a little better. First, let's throw in some kind of color scheme. Ok, but how do the colors map to the bars? Well, if you've read my previous article, you've probably already guessed: converters. We can link together our index value with our color scheme using a converter:

 

[ValueConversion(typeof(int), typeof(Color))]
public class BarColorConverter : IValueConverter
{
static Color[] BarColors = {
Colors.SteelBlue,
Colors.Green,
Colors.Firebrick,
Colors.DarkSlateGray,
Colors.Orange,
Colors.Khaki
};

public object Convert(object value, Type targetType, object parameter,

CultureInfo culture)
{
int v = (int)value;
return BarColors[v % BarColors.Length];
}

public object ConvertBack(object value, Type targetType, object parameter,

CultureInfo culture)
{
Color v = (Color)value;
return Array.IndexOf<Color>(BarColors, v);
}
}

Now, if we want to get fancy, it's also possible to define this converter so that when you use it from XAML you can defined the color scheme similar to a gradient, but this way works too. After defining an instance of our converter in the resources section of our control, we can use it like this:

 

<Grid.Background>
  <SolidColorBrush
    Color="{Binding Path=Index, Converter={StaticResource BarColorConverter}}"/>
</Grid.Background>


That's better, about a Windows Forms level of look and feel, but this isn't Windows Forms. We need to give the bars a more complex look based on their base color. Using converters we can setup a binding that will do this for us. First, let's define a new converter:

 

[ValueConversion(typeof(Color), typeof(Color))]
public class ColorLightnessConverter : IValueConverter
{
public object Convert(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value == null) return null;
int param = int.Parse(parameter.ToString());
Color src = (Color)value;
Color ret = new Color();
ret.A = src.A;
ret.R = (byte)Math.Max(Math.Min(src.R + param, 255), 0);
ret.G = (byte)Math.Max(Math.Min(src.G + param, 255), 0);
ret.B = (byte)Math.Max(Math.Min(src.B + param, 255), 0);
return ret;
}

public object ConvertBack(object value, Type targetType,
object parameter, CultureInfo culture)
{
if (value == null) return null;
int param = int.Parse(parameter.ToString());
Color src = (Color)value;
Color ret = new Color();
ret.A = src.A;
ret.R = (byte)Math.Max(Math.Min(src.R - param, 255), 0);
ret.G = (byte)Math.Max(Math.Min(src.G - param, 255), 0);
ret.B = (byte)Math.Max(Math.Min(src.B - param, 255), 0);
return ret;
}
}

This converter allows us to lighten or darken a databound color as it passes through the binding. It also makes use of the parameter functionality on converters to allow us to specify how much we alter the color from XAML. After we've added the converter to our resource section can use it. First, we need to move output from out other converter, the base color of the bar into somewhere we can easily access; the Tag property is a great place to store random stuff like this so let's use it:

 

Tag="{Binding Path=Index, Converter={StaticResource BarColorConverter}}"

Now we can reference the tag and pass it through our second converter:

 

<Rectangle RadiusX="3" RadiusY="3" StrokeThickness="2" VerticalAlignment="Stretch"
      HorizontalAlignment="Stretch">
  <Rectangle.Stroke>
    <SolidColorBrush Color="{Binding ElementName=Bar, Path=Tag, 
Converter={StaticResource ColorLightnessConverter}, ConverterParameter=-64}"
/> </Rectangle.Stroke> <Rectangle.Fill> <LinearGradientBrush SpreadMethod="Repeat" MappingMode="Absolute"
StartPoint="0,0" EndPoint="1,1"> <LinearGradientBrush.Transform> <ScaleTransform ScaleX="20" ScaleY="20"/> </LinearGradientBrush.Transform> <LinearGradientBrush.GradientStops> <GradientStop
Color="{Binding ElementName=Bar, Path=Tag,
Converter={StaticResource ColorLightnessConverter},
ConverterParameter=-32}"
Offset ="0"/> <GradientStop
Color="{Binding ElementName=Bar, Path=Tag,
Converter={StaticResource ColorLightnessConverter},
ConverterParameter=-32}"
Offset ="0.499"/> <GradientStop Color="{Binding ElementName=Bar, Path=Tag}" Offset ="0.501"/> <GradientStop Color="{Binding ElementName=Bar, Path=Tag}" Offset ="1"/> </LinearGradientBrush.GradientStops> </LinearGradientBrush> </Rectangle.Fill> </Rectangle>

Here, I've created a gradient and border outline based on the base color by darkening it with our converter. This gradient adds the striped appearance to the graph bars so the MappingMode property is important, since it specifies that our gradient is sized relative to the screen not the area it is filling. This prevents the stripes from stretching in an ugly way as the bars change height. Once you do this though, the gradient now becomes 1 point long, hence the scale to 20 points. After adding some more glassy overlays you can see that the final effect is much better, pretty cool for doing everything in databinding eh?

Finally, we can force a special style onto our ListBoxItems, the class that wraps each item in list to turn it into a control:

 

<ListBox.ItemContainerStyle>
  <Style TargetType="{x:Type ListBoxItem}">
    <Setter Property="VerticalContentAlignment" Value="Bottom"/>
    <Style.Triggers>
      <Trigger Property="IsVisible" Value="true">
        <Trigger.EnterActions>
          <BeginStoryboard>
            <Storyboard>
              <DoubleAnimation From="0" To ="1"
                Storyboard.TargetProperty="Opacity" Duration="0:0:0.5"/>
            </Storyboard>
          </BeginStoryboard>
        </Trigger.EnterActions>
      </Trigger>
    </Style.Triggers>
  </Style>
</ListBox.ItemContainerStyle>

This way bars hang at the bottom like they are supposed to and as an added bonus, we can throw in an animation to make the bars fade in as they appear.

Now that the bar graph works, how do we get it to display useful info? Well the most obvious way it to write a method like this:

 

void BenchmarkClick(object sender, RoutedEventArgs e)
{
SolverResult s = new SolverResult();
s.Solver = SolverList.SelectedItem as ISudokuSolver;
int?[,] arr = Board.GameBoard.ToArray();
BenchButton.IsEnabled = false;
long tick = DateTime.Now.Ticks;
s.Failed = !s.Solver.Solve(ref arr);
s.TimeTaken = TimeSpan.FromTicks((DateTime.Now.Ticks - tick));
Graph.Items.Add(s);
BenchButton.IsEnabled = true;
}

The works but there is a problem: solving a grid could take more than a few seconds and this method blocks, causing the UI to freeze until it returns. This is a bad thing. Your users will hate you and your more technical users will point and laugh (you don't believe me, but I've seen it). How can we fix this? The answer is threads! Unfortunately, it's not that easy, there are significant caveats when writing multithreaded code (which are way beyond the scope of this article). Essentially, for our purposes, the problem lies in that you can't interact with the UI objects from another thread! This makes it difficult to say, re-enable the button or update the graph. Luckily, the .NET Framework comes to the rescue with the BackgroundWorker class, although this doesn't completely solve our problem it provides an event which is fired when out background task (our other thread) is complete. Since this event handler runs in our UI thread we can easily interact with those objects. To make things a little more user-friendly I've also added a new hidden panel over the solver information controls that displays a “please wait” message, which we can show while our process is running. The new code looks like this:

 


BackgroundWorker solverWorker;

void BenchmarkClick(object sender, RoutedEventArgs e)
{
SolverResult s = new SolverResult();
s.Solver = SolverList.SelectedItem as ISudokuSolver;
int?[,] arr = Board.GameBoard.ToArray();
BenchButton.IsEnabled = false;
InfoPanel.Visibility = Visibility.Hidden;
WaitPanel.Visibility = Visibility.Visible;
long tick = DateTime.Now.Ticks;
solverWorker = new BackgroundWorker();
solverWorker.DoWork += new DoWorkEventHandler(delegate(object dwsender, DoWorkEventArgs dwe)
{
s.Failed = !s.Solver.Solve(ref arr);
s.TimeTaken = TimeSpan.FromTicks((DateTime.Now.Ticks - tick));
});
solverWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
delegate(object rwcsender, RunWorkerCompletedEventArgs rwce)
{
Graph.Items.Add(s);
InfoPanel.Visibility = Visibility.Visible;
WaitPanel.Visibility = Visibility.Hidden;
BenchButton.IsEnabled = true;
});
solverWorker.RunWorkerAsync();
}

If you're confused, the delegate(object dwsender, DoWorkEventArgs dwe){…} syntax defines an anonymous delegate, a new .NET 2.0 feature. Anonymous delegates are single-use nameless methods. This way we can define our event handlers on the fly without cluttering up our class with extra methods that aren't meant to be called. Also, since anonymous delegates have access to the scope in which they are defined, we can avoid creating lots of temporary variables to hold the objects local to our BenchmarkClick method. On top of that, the program execution conceptually flows in a linear fashion if you ignore the definitions around the delegates, so anonymous delegates also help the function of the method remain clear. In action, this looks like this, and it allows you keep playing sudoku as the solver runs: (It does run slow on my machine though, because much CPU time is spent on the absolutely critical pulsating background featureJ)

I've also added similar threading code the board generation routines and the “I give up” button and a little “x” button to the graph that allows you the clear the current results. Finally, as a finishing touch let's update the extremely dated-looking message box:


I don't know about you, but this dinky non-xp-themed message box doesn't exactly scream “awesome!” at me. To at least partially fix this, I've added a new grid the covers the entire window and is placed in front. By default it's completely invisible, by setting to opacity to 0, and doesn't block input events, by setting the IsHitTestVisible property to false. Then when it's enabled, it springs into action and fades in, tinting the window black and disabling its controls:

 

<Grid IsHitTestVisible="False" IsEnabled="False" x:Name="MessageLayer" 
Opacity="0" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Background="#B0000000"> <Grid.Style> <Style> <Style.Triggers> <Trigger Property="Grid.IsEnabled" Value="True"> <Trigger.EnterActions> <BeginStoryboard> <Storyboard> <DoubleAnimation To="1" Storyboard.TargetProperty="Opacity" Duration="0:0:0.25"/> <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" BeginTime="0:0:0.25"> <DiscreteBooleanKeyFrame KeyTime="0" Value="True"/> </BooleanAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </Trigger.EnterActions> <Trigger.ExitActions> <BeginStoryboard> <Storyboard> <DoubleAnimation To="0" Storyboard.TargetProperty="Opacity" Duration="0:0:0.25"/> <BooleanAnimationUsingKeyFrames Storyboard.TargetProperty="IsHitTestVisible" BeginTime="0:0:0.25"> <DiscreteBooleanKeyFrame KeyTime="0" Value="False"/> </BooleanAnimationUsingKeyFrames> </Storyboard> </BeginStoryboard> </Trigger.ExitActions> </Trigger> </Style.Triggers> </Style> </Grid.Style> </Grid>

Then, in the grid, I placed one of the custom styled expanders defined in our resources section and filled it with a textblock, button and glassy icon. Also I added another hidden expander, which contains a “please wait” message. After defining some new methods to make using these controls easy:

 

void ShowMessage(string m)
{
MessageText.Text = m;
MessageExpander.Visibility = Visibility.Visible;
WaitExpander.Visibility = Visibility.Hidden;
MessageLayer.IsEnabled = true;
}

void ShowWait()
{
MessageExpander.Visibility = Visibility.Hidden;
WaitExpander.Visibility = Visibility.Visible;
MessageLayer.IsEnabled = true;
}

 

and an event handler for the close button on the expander

 

void MessageClosed(object sender, RoutedEventArgs e)
{
MessageLayer.IsEnabled = false;
}

 

We just need to replace MessageBox.Show with ShowMessage to show a fake window:


Hey, as I said before, at least it's more colorful.

At this point, with the work we've done so far and a few extra lines of code you can find in the download, we have a fully functional Sudoku game. I hope you've enjoyed this tutorial series and have some great ideas about a cool WPF app you can build! We've only covered the basics and this is just the tip of the iceberg when it comes to XAML and WinFX. There's lots of cool stuff left like 3D graphics, video and multimedia, databinding to XML, loading XAML at runtime, and browser-based applications to name a few and that's just WPF! If you're itching to code more and want to build on the app, some missing things you might want to work on are:

  • More plug-ins! It supports plug-ins for a reason! Write a better solver that doesn't take forever to run
  • Better message windows that you can actually move around and the ability to dock and move the UI containers
  • Separate the styles and logic more cleanly and implement skins or color schemes
  • Add sound effects or more animations
  • Re-skin ALL the controls for a cooler look and feel
  • Add an internet highscores feature or leaderboards

Remember, if anybody asks, you're not writing a game, you're “updating your workplace skills to match evolving technology”

Tags:

Follow the Discussion

  • MichaelMichael

    The finished product is quite nice, but you overlooked the most important aspect of a Sudoku puzzle: pencil marks.  I was thinking that it is something on feasible with a 9x9 grid.  So here is the question:  Can the game board be duplicated with 9x9 displays inside each square of the 9x9 grid, displayed on top with a very low transparency so that both the board and pencil marks are visible?  You might have to disable the validator on the pencil mark board and expose a TryValue that does validation on the game board without actually setting the value.  Also when a value is set in the game board, the pencil marks are removed, and the validation on the pencil mark board removes that value from the row, collumn, and owning square.

    I'm still learning WPF so I don't know how much I can do programmatically to control such elements as multiple layers on the display and altering who is in front and how is in back and how the transparency value changes as each move back and forth.

    My thought is that when I'm in pencil mark mode, the pencil mark view is in the back with full visibility and the value board being on top and transparent.  When you are back in normal mode, the two flip order and the pencil mark view returns to being transparent while the value board is full visible.  Some animation would make it even cooler...like the transparency between the two boards gradually shifts while there is a halo effect around the board.

  • BruceBruce

    Very fun game, and very fun tutorial.  However, I did find some erros in the logic of the generated numbers to begin a board.  It does not correctly evaluate if a number can be placed in a location without interfearing with the same value elsewhere.  I have not solve the logic yet... but I will update when I find/get around to it.

  • DenverDenver

    Hello Lucas! i've enjoyed this walk through tutorial about wpf! thanks lot Smiley this last installment I managed to encounter an unhandled exception when i tried to benchmark the solver.

    Here's the details:

    System.Windows.Markup.XamlParseException was unhandled

     Message="Cannot find resource named '{BarColorConverter}'. Resource names are case sensitive.  Error at object 'System.Windows.Data.Binding' in markup file 'SudokuFX;component/graphcontrol.xaml' Line 13 Position 116."

     Source="PresentationFramework"

     LineNumber=13

     LinePosition=116

     NameContext="Tag"

     StackTrace:

          at System.Windows.Markup.XamlParseException.ThrowException(String message, Exception innerException, Int32 lineNumber, Int32 linePosition, Uri baseUri, XamlObjectIds currentXamlObjectIds, XamlObjectIds contextXamlObjectIds, Type objectType)

          at System.Windows.Markup.XamlParseException.ThrowException(ParserContext parserContext, Int32 lineNumber, Int32 linePosition, String message, Exception innerException)

          at System.Windows.Markup.BamlRecordReader.ThrowException(SRID id, String parameter)

          at System.Windows.StaticResourceExtension.ProvideValueInternal(IBamlReader bamlReader, Object targetObject, Object targetProperty, Boolean allowDeferredReference)

          at System.Windows.StaticResourceExtension.ProvideValue(IServiceProvider serviceProvider)

          at System.Windows.Markup.BamlRecordReader.ProvideValueFromMarkupExtension(MarkupExtension markupExtension, Object obj, Object member)

          at System.Windows.Markup.BamlRecordReader.BaseReadOptimizedMarkupExtension(Object element, Int16 attributeId, PropertyDefinition propertyDefinition, Object value)

          at System.Windows.Markup.BamlRecordReader.ReadPropertyWithStaticResourceIdRecord(BamlPropertyWithStaticResourceIdRecord bamlPropertyWithStaticResourceIdRecord)

          at System.Windows.Markup.BamlRecordReader.ReadRecord(BamlRecord bamlRecord)

          at System.Windows.StyleHelper.LoadOptimizedTemplateContent(DependencyObject container, ParserContext parserContext, OptimizedTemplateContent optimizedTemplateContent, FrameworkTemplate frameworkTemplate, IComponentConnector componentConnector, IStyleConnector styleConnector, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)

          at System.Windows.FrameworkTemplate.LoadContent(DependencyObject container, List`1 affectedChildren, UncommonField`1 templatedNonFeChildrenField)

          at System.Windows.StyleHelper.ApplyTemplateContent(UncommonField`1 dataField, DependencyObject container, FrameworkElementFactory templateRoot, Int32 lastChildIndex, HybridDictionary childIndexFromChildID, FrameworkTemplate frameworkTemplate)

          at System.Windows.FrameworkTemplate.ApplyTemplateContent(UncommonField`1 templateDataField, FrameworkElement container)

          at System.Windows.FrameworkElement.ApplyTemplate()

          at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

          at System.Windows.UIElement.Measure(Size availableSize)

          at System.Windows.Controls.Border.MeasureOverride(Size constraint)

          at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

          at System.Windows.UIElement.Measure(Size availableSize)

          at System.Windows.Controls.Control.MeasureOverride(Size constraint)

          at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

          at System.Windows.UIElement.Measure(Size availableSize)

          at System.Windows.Controls.StackPanel.MeasureOverride(Size constraint)

          at System.Windows.FrameworkElement.MeasureCore(Size availableSize)

          at System.Windows.UIElement.Measure(Size availableSize)

          at System.Windows.ContextLayoutManager.UpdateLayout()

          at System.Windows.ContextLayoutManager.UpdateLayoutCallback(Object arg)

          at System.Windows.Media.MediaContext.InvokeOnRenderCallback.DoWork()

          at System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()

          at System.Windows.Media.MediaContext.RenderMessageHandlerCore(Object resizedCompositionTarget)

          at System.Windows.Media.MediaContext.RenderMessageHandler(Object resizedCompositionTarget)

          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.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.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.RunInternal(Window window)

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

          at System.Windows.Application.Run()

          at SudokuFX.MyApp.Main() in D:\Documents and Settings\mon\Desktop\WPF Sudoku\262086_SudokuFX5\SudokuFX5\SudokuFX\obj\Debug\MyApp.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()

    Any hint, about it? I've tried to find "{BarColorConverter}"  but couldn't find it.

    Hope you could extend your help about this, thanks! Smiley

  • daemondaemon

    Hello Lucas! I'm having a problem following your tutorial on Sudoku harnessing WPF. Actually for several times all of the five source codes you provided was working well then just yesterday the designer on "Windows1.xaml" won't show up anymore. Here's the error provided by the editor.

    • Error 1 Assembly '' was not found. The 'clr-namespace' URI refers to an assembly that is not referenced by the project. D:\Documents and Settings\mon\Desktop\Sudoku Madness\Sudoku Madness\Window1.xaml 4 15 Sudoku Madness

    • Error 2 The type 'clr:MadBoard' was not found. Verify that you are not missing an assembly reference. D:\Documents and Settings\mon\Desktop\Sudoku Madness\Sudoku Madness\Window1.xaml 92 13 Sudoku Madness

    • Warning 3 The element 'DockPanel' in namespace 'http://schemas.microsoft.com/winfx/2006/xaml/presentation' has invalid child element 'MadBoard' in namespace 'clr-namespace:Sudoku_Madness'. List of possible elements expected: 'DockPanel.LastChildFill, DockPanel.Background, DockPanel.IsItemsHost, DockPanel.Style, DockPanel.OverridesDefaultStyle, DockPanel.Triggers, DockPanel.Resources, DockPanel.DataContext, DockPanel.Language, DockPanel.Tag, DockPanel.InputScope, DockPanel.LayoutTransform, DockPanel.Width, DockPanel.MinWidth, DockPanel.MaxWidth, DockPanel.Height, DockPanel.MinHeight, DockPanel.MaxHeight, DockPanel.Margin, DockPanel.FocusVisualStyle, DockPanel.Cursor, DockPanel.ForceCursor, DockPanel.ToolTip, DockPanel.ContextMenu, DockPanel.InputBindings, DockPanel.CommandBindings, DockPanel.AllowDrop, DockPanel.RenderSize, DockPanel.RenderTransform, DockPanel.RenderTransformOrigin, DockPanel.Opacity, DockPanel.OpacityMask, DockPanel.BitmapEffect, DockPanel.BitmapEffectInput, DockPanel.ClipToBounds, DockPanel.Clip, DockPanel.SnapsToDevicePixels, DockPanel.IsEnabled, DockPanel.IsHitTestVisible, DockPanel.Focusable, sgUIElement, sgFrameworkElement, sgShape, Ellipse, Line, Path, Polygon, Polyline, Rectangle, sgPanel, Canvas, DockP.... D:\Documents and Settings\mon\Desktop\Sudoku Madness\Sudoku Madness\Window1.xaml 92 13 Sudoku Madness

    I'm currently new to WPL and XAML, I tried to look for some clues on how to fix it but I can't avail anything. I'm using Visual Studio 2005 Pro, Running on Win XP SP2, I've installed also the extensions for Windows Workflow Foundation and the CTP 2006 of .NET 3.0

    I'm hoping you could help me address this issue and if you would be so kind at least elaborate why such thing happen. Thanks Smiley

    *I'm wondring if there is such compatibility issues between the cde generated by the Visual Studio Express edition and the Professional Edition which I'm using currently.

  • monzmonz

    when i tried to benchmark the solvers, i got an exception about the bar color coverter, im quite sure i havent altered any line.

    any suggestion?

  • Clint RutkasClint I'm a "developer"

    Sudoku challenge:

    Part 1:

    http://blogs.msdn.com/coding4fun/archive/2006/11/06/999502.aspx

    Part 2:

    http://blogs.msdn.com/coding4fun/archive/2006/11/06/999781.aspx

    Part 3:

    http://blogs.msdn.com/coding4fun/archive/2006/11/30/1178193.aspx

    Part 4:

    http://blogs.msdn.com/coding4fun/archive/2006/11/30/1178206.aspx

    Part 5:

    http://blogs.msdn.com/coding4fun/archive/2006/11/30/1178235.aspx

  • Software Information » Coding4Fun : Building a WPF Sudoku Game Part 1: Introduction to WPF &#8230;Software Information » Coding4Fun : Building a WPF Sudoku Game Part 1: Introduction to WPF …

    PingBack from http://softwareinformation.247blogging.info/coding4fun-building-a-wpf-sudoku-game-part-1-introduction-to-wpf/

  • GavinGavin

    Even over two years after this series of articles was published, it's still serving as a useful tutorial on WPF. Thanks for the good work in putting this together.

    A few people have commented on an exception being raised when you try to benchmark a solver plugin - static resource BarColorConverter could not be found.

    I've found that you need to move the two lines:

    <clr:BarColorConverter x:Key="BarColorConverter"/>

    <clr:ColorLightnessConverter x:Key="ColorLightnessConverter"/>

    from the position at the end of the <ListBox.Resources> declaration in GraphControl.xaml to the beginning of the <ListBox.Resources> declaration - I moved them to right under <ListBox.Resources>. I haven't experimented with any alternative locations.

    The above fix works for me. Hope it helps!

  • ge-forcege-force

    This is great - am working on enhancing it (3D bevel effect, even more custom controls, transparancy, ect.)

    I'm a newbe to WPF, but this tutorial really helped.

    Thanks!

    Ge-force

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.