Feed Subscribe


VS2010 Bug: Entity Data Model Wizard disappears after selecting “Generate from Database”

by ondrejsv 6. June 2010 17:55

Today I found another annoying bug in Visual Studio 2010. I often work connected to my company LAN via VPN and I have generated some entity data models from databases over the wire in the past. Entity Data Model Wizard which runs when you add a new “ADO.NET Entity Data Model” item in Visual Studio has a “nice feature” that it remembers all connections you entered into it. Of course, if you disconnect from LAN, those connections are not available. If you are unlucky enough, one of those connections may be the first one and default in the combobox where you can select an existing connection to base your EDMX on. Visual Studio probably tries to get information on tables or whatever from the database but if the database is not reachable, it fails very silently and closes the window instead of letting you to select another connection or create a new one. #fail

Fortunately, these connections are the same you see in the Data Connections node of the Server Explorer. So delete all of those which are remote and you have no connection to them any more and the Data Model Wizard would behave properly.

Tags:

How to debug PHP applications on Windows

by ondrejsv 2. June 2010 00:20

If you need to dwelve into some PHP stuff and do not want to leave your comfortable Windows environment, just read on. It’s easy and no Apache will be harmed in this process.

Disclaimer: It’s been almost 10 years since I wrote the last serious line in PHP (PHP 3 then), so it’s possible that there is another way to debug PHP applications, or that I do not do things ideally or in the most elegant way but it did work for me.

You can install PHP conveniently and make it play nicely with IIS via the Microsoft Web Platform Installer. You then need some IDE to edit and debug the application. Microsoft Expression Web supports PHP but obviously not debugging. If you don’t mind spending some bucks, you can buy VS.Php or other similar product and use Visual Studio to do all PHP magic and stop reading. Or you may touch the other side of the world and download Eclipse with PHP Developer Tools (PDT) embedded. Unpack it but do not run yet. Download XDebug tool; note that you must download version that matches your PHP version and build flavor (if you don’t know, they have a fantastic tool that tells you what you want). Unpack it to any folder of your choice, open PHP.ini (by default in C:\Program Files\PHP) and at the very end of the file paste these lines:

[xdebug] xdebug.remote_enable=1 xdebug.remote_host="localhost" xdebug.remote_port=9000 xdebug.remote_handler="dbgp" zend_extension = C:\Program Files\php\plugins\php_xdebug-2.1.0RC1-5.2-vc6-nts.dll

Of course, the path in the last line should reflect the folder you unpacked XDebug zip into. Also note that if you have some non-standard build of PHP (on Windows it means something else than non-debug non-thread-safe), you may need to change the last line – read full instructions.

Verify that XDebug is correctly installed by running php.exe –m. The last line should read:

[Zend Modules] Xdebug

Restart IIS (iisreset) and now you may fire Eclipse. Go to Windows/Preferences/PHP/Debugger and change it from default Zend Debugger to XDebug.

Create a new file, type in some PHP code, set a breakpoint (Ctrl+Shit+B? What the…? I must find a way to change its key bindings to Visual Studio ones) and run. Yes, it works :-) :

image

Tags:

WebClientFactoryPackage2 not found when upgrading from WCSF 2.0 to WCSF 2010

by ondrejsv 26. May 2010 20:39

patterns & practices team has released final version of their Web Client Software Factory. After you have migrated your solution to Visual Studio 2010, you may encounter this message:

image

Framework manifest contains location for package WebClientFactoryPackage2 configuration file, but the file was not found. Do you want to permanently remove the package from the current solution?

If you want to definitely convert to Visual Studio 2010, you may safely answer Yes. This error is caused by references in the solution.gpState file to the old version of the software factory. After removing you must reenable the WCSF in the Tools –> Guidance Package Manager –> Enable/Disable Packages in Visual Studio. Then references to the WCSF 2010 will be created (WebClientGuidancePackage2010).

Tags:

Silverlight DataForm’s autogenerated fields send empty strings to database

by ondrejsv 28. March 2010 17:18

Silverlight Toolkit comes with a great control for quick building typical business application screens – DataForm, a sibling control to the DataGrid for showing and editing a single record. If you use its field autogeneration feature, you may be surprised when looking to the database after its first submit. All your ((n)var)char columns rendered as TextBoxes in the DataForm that had been empty have also empty string values in the database:

image

This is not what you usually want. If the field is left blank, I expect the database field be NULL instead of an empty string. So what to do to persuade the DataForm to send nulls in this case?

An IValueConverter, of course! First we define a suitable one which returns null when converting back an empty string:

public class SourceNullValueConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { return value == null ? "" : value.ToString(); } public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { if (targetType != typeof(string)) return value; if (value == null) return null; return value.ToString() == "" ? null : value.ToString(); } }

Secondly, we need to force the DataForm to use this value converter instead of its own default DataFormToStringConverter. We do this by handling AutoGeneratingField event where we get the already built binding, copy it (by using CreateCopy extension method from the Silverlight Business Application template) and set our new value converter:

private void EmployeeForm_AutoGeneratingField(object sender, DataFormAutoGeneratingFieldEventArgs e) { if (e.Field.Content.GetType() == typeof(TextBox)) { Binding binding = e.Field.Content.GetBindingExpression(TextBox.TextProperty).ParentBinding.CreateCopy(); binding.Converter = new SourceNullValueConverter(); e.Field.Content.SetBinding(TextBox.TextProperty, binding); } }
image 

Case closed, Poirot!

kick it on DotNetKicks.com

Tags: ,

Easily migrate MySQL database into SQL Server

by ondrejsv 27. March 2010 20:11

If you want to migrate for whatever reason your MySQL database to SQL Server, forget all commercial tools or pesky DTS or SSIS packages! There’s a gem on the Microsoft SQL Server site called in the sterile marketing language Microsoft SQL Server Migration Assistant for MySQL or better SSMA for MySQL (currently CTP). Grab it from the Migration page.

image

Migration process has two steps – first you convert MySQL schema into corresponding SQL Server structures. You can either create tables directly on SQL Server instance or save the schema into file and run it later. In the second step you copy data. Everything in two clicks!

You can change some options regarding conversion between types and other and also you have choice to select just objects you are interested to migrate.

Two clicks and voila:

image

There are similar tools for migrating from Oracle, Sybase and Access on the Microsoft web site. Hopefully they give us a PostgreSQL one, too.

kick it on DotNetKicks.com

Tags: ,

How to: Create computed/custom properties for sample data in Blend/Sketchflow

by ondrejsv 14. March 2010 20:32

I blogged about sample data in Microsoft Blend/SketchFlow previously. SketchFlow is a great tool for rapidly building interactive screen prototypes. Sample data feature helps you to create plausible screen mocks quickly. I want to emphasize words interactive prototypes. Yes, user can click here and there and sees the entire “application flow”. Previously we mocked screens in HTML, had them rendered and sent this to our customers as a package full of JPEGs. Almost as a rule we had small disputes with customers who were arguing that they just cannot “grasp the application” from JPEGs. Now we can just publish a Silverlight prototype to our server and customers browse it throughout.

Hopefully I don’t need to note that logically connected screens should play together. Having double clicked a record in a datagrid on one screen and being redirected to another screen showing detailed information on this only record, data in fields should be the same (or related) to the record I clicked on the first screen. If you use sample data, it means that you should bind the first and second screen against the very same data source.

One problem I encountered is managing data derived from other fields in sample collection. Let’s say I have a customers collection with usual data like first name, last name and birth date. The first screen shows this list but I want to have only one column for customer name showing first and last name concatenated. I need them separated in my collection because the second “customer detail” screen would show them in separate text boxes. I could create a new string field and manually type in the full name but I am lazy to do such work and frankly have personal objections to do so. I prefer the way the full name field just computes itself. That way I can modify either name and full name updates automatically.

image

Double clicking on the Natasha row would bring me to the second screen with separate fields:

image

Fortunately, this task is not difficult. Your sample data collections are stored as XML files inside the SampleData folder in the XXXScreens project. For each such XML file, Blend generates a corresponding C# file with two ordinary class, one representing the collection and one the collection item (named Item):

image

public class Employees : System.ComponentModel.INotifyPropertyChanged { public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } public Employees() { try { System.Uri resourceUri = new System.Uri("/SilverlightPrototype_Derived.Screens;component/SampleData/Employees/Employees.xaml", System.UriKind.Relative); if (System.Windows.Application.GetResourceStream(resourceUri) != null) { System.Windows.Application.LoadComponent(this, resourceUri); } } catch (System.Exception) { } } private ItemCollection _Collection = new ItemCollection(); public ItemCollection Collection { get { return this._Collection; } } } public class Item : System.ComponentModel.INotifyPropertyChanged { public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { if (this.PropertyChanged != null) { this.PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); } } private string _LastName = string.Empty; public string LastName { get { return this._LastName; } set { if (this._LastName != value) { this._LastName = value; this.OnPropertyChanged("LastName"); } } } private string _FirstName = string.Empty; public string FirstName { get { return this._FirstName; } set { if (this._FirstName != value) { this._FirstName = value; this.OnPropertyChanged("FirstName"); } } } }

Pretty boring code. If only the Item class would be generated with the partial directive, we could add our own class with the FullName computed property.

Let’s start some hacking then. Close Blend, start your favorite text editor and open the file C:\Program Files\Microsoft Expression\Blend 3\Templates\en\SampleDataCode.cs. This is the template Blend uses for generating C# code from the sample data XML source file.

Change line 43 from:

public class COMPOSITE_TYPE : System.ComponentModel.INotifyPropertyChanged //CompositeTypeHeader - BEGIN

to:

public partial class COMPOSITE_TYPE : System.ComponentModel.INotifyPropertyChanged //CompositeTypeHeader - BEGIN

Now start Blend again, open your project, force Blend to regenerate the code file (by adding a new property and removing it immediately). If you open the sample data code file, you can notice that the Item class now has the partial keyword!

Add a new class, change the namespace to exactly math the one in the original code file and write your own partial class, like mine:

namespace Expression.Blend.SampleData.Employees { public partial class Item { public string FullName { get { return string.Format("{0} {1}", FirstName, LastName); } } } }

Job done! I may create a new bound column to my datagrid:

<data:DataGridTextColumn Header="Full Name" Binding="{Binding FullName}"/>

Note: Blend may not recognize your new properties, so you may need to write your bound fields in XAML yourself, not in the Blend UI.

kick it on DotNetKicks.com

Tags: ,

How to: Create a sketchy Siverlight GroupBox in Blend/SketchFlow

by ondrejsv 17. January 2010 18:17

Silverlight does not come with a groupbox control in its standard set of controls. Fortunately, it’s not so difficult to create a new one or you may grab Tim Greenfield’s Silverlight GroupBox control.

Having Tim’s control referenced, open the SketchStyles.xaml file in your screen mocks project, switch to the XAML view and at the end of the file before the final </ResourceDictionary> tag append this new style:

<Style x:Key="GroupBox-Sketch" TargetType="Groupbox:GroupBox">
<Setter Property="Background" Value="{StaticResource BaseBackground-Sketch}"/>
<Setter Property="Foreground" Value="{StaticResource BaseForeground-Sketch}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="{StaticResource BaseBorder-Sketch}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Groupbox:GroupBox">
<Grid Background="{TemplateBinding Background}">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<SketchControls:SketchRectangleSL Grid.Row="1" Grid.RowSpan="2" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<SketchControls:SketchRectangleSL.Clip>
<GeometryGroup FillRule="EvenOdd">
<RectangleGeometry x:Name="FullRect" Rect="0,0,300,200"/>
<RectangleGeometry x:Name="HeaderRect" Rect="6,0,100,100"/>
</GeometryGroup>
</SketchControls:SketchRectangleSL.Clip>
</SketchControls:SketchRectangleSL>
<ContentPresenter Margin="{TemplateBinding Padding}" Grid.Row="2" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
<ContentControl x:Name="HeaderContainer" HorizontalAlignment="Left" Margin="6,0,0,0" Grid.Row="0" Grid.RowSpan="2">
<ContentPresenter Margin="3,0,3,0" Content="{TemplateBinding Header}" ContentTemplate="{TemplateBinding HeaderTemplate}"/>
</ContentControl>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FontFamily" Value="{StaticResource FontFamily-Sketch}"/>
<Setter Property="FontSize" Value="{StaticResource SizeDouble-Sketch}"/>
<Setter Property="FontWeight" Value="{StaticResource FontWeight-Sketch}"/>
</Style>

At the beginning of the file add this new namespace declaration:

xmlns:Groupbox="clr-namespace:Groupbox;assembly=Groupbox"

Done! You may now create a sketchy groupbox:

image

image

kick it on DotNetKicks.com

Tags: ,

How to: Create a new format for sample data in Blend/SketchFlow

by ondrejsv 17. January 2010 14:04

Microsoft Expression SketchFlow (part of the Expression Blend Product) is a great tool for quickly creating interactive low-fidelity prototypes. One of its features is sample data. You can either import data from an XML file, type in it manually within the SketchFlow user interface or let it populate automatically from the Blend sample data collection. The last option is very effective if you are experimenting with many screen mocks.

Note: You may use sample data feature equally well in the SketchFlow as well as in the Blend itself.

Blend supports four data types in sample data: string, number, boolean and image. If you create a new string property you can tell Blend what format you want to have the property in – e.g. name, url address, e-mail or color. Blend then create a sample data based on this (in our case it may be “Jamison, Jay”, http://www.graphicdesigninstitute.com/, someone-3@adventure-works.com, #FFFFED6F).

You can also create a new format if you often create screen mocks with some specific data. For example let’s say that I often mock administration module prototypes and I would find useful to have a login name format to let Blend create sample data for me.

All sample data is stored in the C:\Program Files\Microsoft Expression\Blend 3\SampleDataResources\en\Data\SampleStrings.csv.

Tip: If you are running Blend in other language than English, you may create a subfolder with your language code (instead of the en) and it takes precedence over the en folder.

This file is nothing more that a comma separated data file. I can add a new column to the header and then new columns do data rows (new columns are last):

Name,Phone Number,Email Address,Website URL,Address,Company Name,Date,Time, Price, Colors,Login
"Aaberg, Jesper",(111) 555-0100,someone@example.com,http://www.adatum.com/,"4567 Main St., Buffalo, NY 98052",A. Datum Corporation,"November 5, 2003",8:20:14 AM,$100,#FF8DD3C7,jesper.aaberg
"Adams, Ellen",(222) 555-0101,user@adventure-works.com,http://www.adventure-works.com/,"1234 Main St., Buffalo, NY 98052",Adventure Works,"December 29, 2006",7:06:05 AM,$29.99,#FFFFFFB3,ellen.adams
"Adams, Terry",(333) 555-0102,someone@adventure-works.com,http://www.adventure-works.com/ ,"2345 Main St., Buffalo, NY 98052",Adventure Works,"January 19, 2004",4:26:00 PM,$249.99,#FFBEBADA,terry.adams

and so on. Just remember to restart Blend if you finish modifying the sample dataset.

Note: You don’t need to supply a sample value for all rows but be sure to include a comma then. Otherwise Blend won’t load the file correctly.

After restart I can create a new string property with my new Login format:

image

and bind it to a DataGrid or any other control:

image

Happy mocking! :-)

kick it on DotNetKicks.com

Tags: ,

Audio recorder Silverlight 4 sample

by ondrejsv 12. December 2009 14:48

Today I want to share with you my Audio Record sample using some new cool features coming in Silverlight 4. The sample demonstrates:

  • recording audio using the AudioCaptureDevice and CaptureSource classes, and saving it using an AudioSink object,
  • access to local folders on the computer,
  • simple commanding model using the new Command properties and ICommand interface,
  • changing appearance of a standard control (button) using its visual states.

Of course, full source code is attached at the end of the article.

The user interface is really simple:

image

What we want to achieve:

  1. three state audio recorder with three buttons “Record”, “Stop” and “Save”,
  2. the user first selects the “Record” buttons, the recorder will begin recording; the user clicks the “Stop” button when he is done with recording and finally selects the “Save” button to be prompted by a classic Save file dialog to save the record into a standard WAV file on his disc,
  3. command buttons should have a correct enabled/disabled logic,
  4. a status bar with text helping the user what to do or what is the recorder doing,

Disclaimer: Obviously I’m not the one gifted with graphical skills so don’t expect nifty animations here and there. It’s just a sample. :-)

Recording audio

An AudioCaptureDevice object represents a hardware device cable capturing sound. There may be more than one present and the user may select a default one in the new Silverlight 4 configuration tab:

image

You can get the default one through the CaptureDeviceConfiguration class:

var audioDevice = CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice();

Then you use a CaptureSource object to capture any input from this source:

_captureSource = new CaptureSource() { AudioCaptureDevice = audioDevice };

Lastly, we need to connect a sink to the capture source. Sinks allow us to process incoming input from the source in any way we want – for example to store in a memory stream. For this we derive a new AudioSink (our is called MemoryAudioSink) and override the most important method – OnSamples:

protected override void OnSamples(long sampleTime, long sampleDuration, byte[] sampleData) { // New audio data arrived, write them to the stream. _stream.Write(sampleData, 0, sampleData.Length); }

To connect and start capturing the sound:

_sink = new MemoryAudioSink(); _sink.CaptureSource = _captureSource; _captureSource.Start()

Writing a WAV file

CaptureSource sends the captured sound to its sinks as raw PCM data. We need to add a standard WAV header to be usable for a user. This is done by the SavePcmToWav method:

public static void SavePcmToWav(Stream rawData, Stream output, AudioFormat audioFormat) { if (audioFormat.WaveFormat != WaveFormatType.PCM) throw new ArgumentException("Only PCM coding is supported."); BinaryWriter bwOutput = new BinaryWriter(output); // Write down the WAV header. // Refer to http://technology.niagarac.on.ca/courses/ctec1631/WavFileFormat.html // for details on the format. // Note that we use ToCharArray() when writing fixed strings // to force using the char[] overload because // Write(string) writes the string prefixed by its length. // -- RIFF chunk bwOutput.Write("RIFF".ToCharArray()); // Total Length Of Package To Follow // Computed as data length plus the header length without the data // we have written so far and this data (44 - 4 ("RIFF") - 4 (this data)) bwOutput.Write((uint)(rawData.Length + 36)); bwOutput.Write("WAVE".ToCharArray()); // -- FORMAT chunk bwOutput.Write("fmt ".ToCharArray()); // Length Of FORMAT Chunk (Binary, always 0x10) bwOutput.Write((uint)0x10); // Always 0x01 bwOutput.Write((ushort)0x01); // Channel Numbers (Always 0x01=Mono, 0x02=Stereo) bwOutput.Write((ushort)audioFormat.Channels); // Sample Rate (Binary, in Hz) bwOutput.Write((uint)audioFormat.SamplesPerSecond); // Bytes Per Second bwOutput.Write((uint)(audioFormat.BitsPerSample * audioFormat.SamplesPerSecond * audioFormat.Channels / 8)); // Bytes Per Sample: 1=8 bit Mono, 2=8 bit Stereo or 16 bit Mono, 4=16 bit Stereo bwOutput.Write((ushort)(audioFormat.BitsPerSample * audioFormat.Channels / 8)); // Bits Per Sample bwOutput.Write((ushort)audioFormat.BitsPerSample); // -- DATA chunk bwOutput.Write("data".ToCharArray()); // Length Of Data To Follow bwOutput.Write((uint)rawData.Length); // Raw PCM data follows... // Reset position in rawData and remember its origin position // to restore at the end. long originalRawDataStreamPosition = rawData.Position; rawData.Seek(0, SeekOrigin.Begin); // Append all data from rawData stream into output stream. byte[] buffer = new byte[4096]; int read; // number of bytes read in one iteration while ((read = rawData.Read(buffer, 0, 4096)) > 0) { bwOutput.Write(buffer, 0, read); } rawData.Seek(originalRawDataStreamPosition, SeekOrigin.Begin); }

Simple commanding with a simple view model

I’m a fan of the new Model-View-ViewModel pattern as it allows you further properly structure your presentation layer while taking advantage of powerful WPF/Silverlight databinding. It wasn’t so simple to use it in Silverlight projects because it did not support commands as the full WPF stack did so you needed to resort to Prism or other frameworks.

However, with Silverlight 4 comes the ICommand interface:

public interface ICommand { event EventHandler CanExecuteChanged; bool CanExecute(object parameter); void Execute(object parameter); }

and new Command properties on ButtonBase-derived controls and the HyperLink control.

With this you can create your own viewmodel wrapping all your code interacting with the user interface and application logic in a single unit-testable class. The user interface will hook into your viewmodel class though public ICommand and dependency properties by leveraging databinding. Your code-behind will be almost empty!

To simplify things I created a SimpleCommand because all of my commands will just call a method on the viewmodel class and I will set their executability directly through a property:

public class SimpleCommand : ICommand { public Action ExecuteAction { get; set; } private bool _canExecute; public bool MayBeExecuted { get { return _canExecute; } set { if (_canExecute != value) { _canExecute = value; if (CanExecuteChanged != null) CanExecuteChanged(this, new EventArgs()); } } } #region ICommand Members public bool CanExecute(object parameter) { return MayBeExecuted; } public event EventHandler CanExecuteChanged; public void Execute(object parameter) { if (ExecuteAction != null) ExecuteAction(); } #endregion }

Let’s dig into the viewmodel class – the heart of the application. First some declarations:

public class RecorderViewModel : DependencyObject { private SimpleCommand _recordCommand; private SimpleCommand _playPauseCommand; private SimpleCommand _saveCommand; private MemoryAudioSink _sink; private CaptureSource _captureSource; private SaveFileDialog saveFileDialog = new SaveFileDialog() { Filter = "Audio files (*.wav)|*.wav" }; public SimpleCommand RecordCommand { get { return _recordCommand; } } public SimpleCommand PlayPauseCommand { get { return _playPauseCommand; } } public SimpleCommand SaveCommand { get { return _saveCommand; } } public static readonly DependencyProperty StatusTextProperty = DependencyProperty.Register("StatusText", typeof(string), typeof(RecorderViewModel), null); public string StatusText { get { return (string)GetValue(StatusTextProperty); } set { SetValue(StatusTextProperty, value); } }

The constructor initializes the three commands to just call their respective methods on the class:

public RecorderViewModel() { _recordCommand = new SimpleCommand() { MayBeExecuted = true, ExecuteAction = () => Record() }; _saveCommand = new SimpleCommand() { MayBeExecuted = false, ExecuteAction = () => SaveFile() }; _playPauseCommand = new SimpleCommand() { MayBeExecuted = false, ExecuteAction = () => PlayOrPause() }; var audioDevice = CaptureDeviceConfiguration.GetDefaultAudioCaptureDevice(); _captureSource = new CaptureSource() { AudioCaptureDevice = audioDevice }; GoToStartState(); }

The last piece is to define the methods doing the hard work:

protected void Record() { if (!EnsureAudioAccess()) return; if (_captureSource.State != CaptureState.Stopped) return; _sink = new MemoryAudioSink(); _sink.CaptureSource = _captureSource; _captureSource.Start(); // Enable pause command, disable record command _playPauseCommand.MayBeExecuted = true; _recordCommand.MayBeExecuted = false; StatusText = "Recording..."; } protected void PlayOrPause() { if (_captureSource.State == CaptureState.Started) { _captureSource.Stop(); // Disable pause command, enable save command _playPauseCommand.MayBeExecuted = false; _saveCommand.MayBeExecuted = true; StatusText = "Recording finished. You may save your record."; } } protected void SaveFile() { if (saveFileDialog.ShowDialog() == false) { return; } StatusText = "Saving..."; Stream stream = saveFileDialog.OpenFile(); WavManager.SavePcmToWav(_sink.BackingStream, stream, _sink.CurrentFormat); stream.Close(); MessageBox.Show("Your record is saved."); GoToStartState(); }

Putting it all together

I put three buttons and one textblock into the default MainPage.xaml:

<Button Command="{Binding RecordCommand}" Grid.Column="0" HorizontalAlignment="Left" Name="button1" Template="{StaticResource RecordCommandStyle}"/>
<Button Content="Stop" Command="{Binding PlayPauseCommand}" Grid.Column="1" HorizontalAlignment="Left" Name="button3" Width="75" Template="{StaticResource StopCommandStyle}"/>
<Button Content="Save" Command="{Binding SaveCommand}" Grid.Column="2" HorizontalAlignment="Left" Name="button2" Width="57" Margin="0,0,0,-1" Template="{StaticResource SaveCommandStyle}"/>
<TextBlock Text="{Binding StatusText}" Name="textBlock1" Margin="8,5,-32,8" Grid.Row="1" Grid.ColumnSpan="4" Foreground="#FF13E3BC" FontSize="12" />

Note the bindings to the Command properties of the buttons and Text property of the textblock element (but this is nothing special). The whole viewmodel is then set to the page’s DataContext which is the only thing we do inside the code-behind:

RecorderViewModel ViewModel; public MainPage() { InitializeComponent(); ViewModel = new RecorderViewModel(); DataContext = ViewModel; }

That’s it. I didn’t show changing the appearance of the standard button controls using visual states here because this possibility has been with us long before the fourth version but it’s included in the project.

The floppy disk icon comes from the wonderful Crystal project.

Download full sources of the sample.

kick it on DotNetKicks.com

Tags:

Globalization is not an enemy

by ondrejsv 9. December 2009 18:36

System.FormatException: Input string was not in a correct format. The exception I see again and again because I’m running an English Windows 7 with Slovak regional settings and some developers just can’t imagine there are regions in the world where the decimal separator is not a period (.) but a comma (,) or where they use more than 12 hours a day. It’s unfortunate for them because many more people live outside the USA so reach of their application is far less than it could be.

Today I want to build a demonstration utilizing a Ribbon bar in my Silverlight application and I found one. A sample was attached but it crashed immediately after start with the aforementioned exception. The Ribbon control stores some default brushes in an XML file and parses on its own with code like this:

string xStr = pointAsString.Substring(0, pointAsString.IndexOf(","));
string yStr = pointAsString.Substring(pointAsString.IndexOf(",") + 1);

return new Point(double.Parse(xStr), double.Parse(yStr));

If we take a look at a fragment of the XML:

<LinearGradientBrush Name="GroupedButtonSemiHighlight" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#FFFEF7" Offset="0.0" />
<GradientStop Color="#FFF7DF" Offset="0.5" />
<GradientStop Color="#FFEEBE" Offset="0.6" />
<GradientStop Color="#FFF6DD" Offset="1.0" />
</LinearGradientBrush>

we see that it cannot succeed on any client configuration which does not use a point as decimal separator because the double.Parse method without providing an IFormatProvider would parse the string using the current regional settings.

If you write code that parses numbers from a content not edited or viewable directly by users and don’t want to take regional settings into account, always use the Invariant Culture as your IFormatProvider.

So I fixed the code:

string xStr = pointAsString.Substring(0, pointAsString.IndexOf(","));
string yStr = pointAsString.Substring(pointAsString.IndexOf(",") + 1);

return new Point(double.Parse(xStr, CultureInfo.InvariantCulture), double.Parse(yStr, CultureInfo.InvariantCulture));

More detailed information on internationalization, globalization and localization issues can be found on the Globalization Step By Step Microsoft site.

kick it on DotNetKicks.com

Tags: