tag:blogger.com,1999:blog-59562825636436001792024-03-08T04:09:45.677-06:00insertAlias.Write("...");<p>Maybe some code, maybe some observations, maybe some complaining.</p>
Maybe some updates every now and then.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.comBlogger22125tag:blogger.com,1999:blog-5956282563643600179.post-91973295020578209652011-03-18T15:37:00.000-05:002011-03-18T15:37:14.572-05:00ScreenGrabber and GithubThe screenshotting program that I've been working on for so long is finally nearing completion. I've updated it to include a lot of neat features, and I've gotten rid of the stupid way that I was capturing screen areas, replaced with being able to draw a box around what you want.<br />
<br />
It's too big to just post the code here, and I really can't upload anything here anyway. So I'll use this to also mention that I have a Github account. My repos are here:<br />
<br />
https://github.com/insertAlias<br />
<br />
ScreenGrabber has my screen shot program.<br />
<br />
Download it, build it, and break it for me so I can make it better!<br />
<br />
Also, if anyone wants to make any better icons for me, I'd love them, since mine suck.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-72223123984511814232011-03-10T10:21:00.001-06:002011-03-10T10:22:42.557-06:00New Extension Method for IList: SafeInsert<a href="http://msdn.microsoft.com/en-us/library/system.collections.ilist.insert.aspx">IList.Insert</a> takes an index and a value, and attempts to insert that value at that index, pushing all following values out to the next index. Of course, that index must exist to do the insert.<br />
<br />
I figured, why? If I want to move something to position 50, but there are only 5 values in the List, is it reasonable to assume that I want it to go to the last spot, since 50 isn't available? Maybe. Especially in the case where I'm presenting the user a grid, and allowing them to change a row's index manually. I'd rather not bother testing the number they enter, and just assume that if they put an index greater than exists that they want it to be inserted at the end.<br />
<br />
This really isn't challenging code or anything, but it's useful, and the first time I thought of it, so I'll share:<br />
<br />
<pre class="brush: c-sharp">public static void SafeInsert<t>(this IList<t> list, int index, T val){
if (index < 0)
list.Insert(0, val);
else if (index > list.Count)
list.Insert(list.Count, val);
else
list.Insert(index, val);
}
</pre>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-47736482190711306922011-02-11T13:07:00.001-06:002011-02-11T13:08:16.465-06:00Extension methods: For<T> and ForEach<T>I noticed that List<t> has a method called ForEach, that takes an Action<t> and applies it to all elements in the collection. This is not an extension method, it's part of List.<br />
<br />
I wondered why it's not part of the LINQ Extension methods. The best I could come up with was that IEnumerables can be infinite, so a non-terminable ForEach would cause problems. Of course, we can do these with standard foreach loops, so I see no harm in including it if you know what you're getting into. Also, it should be possible to provide a terminator clause to exit the foreach loop as a Func<T, bool>.<br />
<br />
With that said, I also felt the need to be able to simulate a simple for loop. An indexed-foreach, if you will. So here are three extension methods I've come up with:<br />
<br />
<pre class="brush: c-sharp">public static void For<T>(this IEnumerable<T> items, Action<int, T> action) {
for (int i = 0; i < items.Count(); i++)
action(i, items.ElementAt(i));
}
public static void ForEach<T>(this IEnumerable<T> items, Action<T> action) {
foreach (T item in items)
action(item);
}
public static void ForEach<T>(this IEnumerable<T> items, Action<T> action, Func<T, bool> breakWhen) {
foreach (T item in items) {
if (breakWhen(item))
break;
action(item);
}
}
</pre><br />
Here's an example of use:<br />
<br />
<pre class="brush: c-sharp">var numbers = Enumerable.Range(10, 10);
numbers.For((i, x) => Console.WriteLine("Index: {0}, Value: {1}", i, x));
numbers.ForEach(x => Console.WriteLine(x));
var allReal = Extensions.Real(0);
allReal.ForEach(x => Console.WriteLine(x), x => x > 9);
</pre><br />
<span style="background-color: lightgrey;">Extensions.Real(0)</span> is just a method that returns an IEnumerable<int> that starts at 0 and counts up. There's no exit condition, so if you tried to iterate through the whole thing, it'd just go until it hit an integer overflow. But it shows we can provide an exit condition for a ForEach loop.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-74301810506729472422010-12-21T14:57:00.002-06:002010-12-22T00:34:44.891-06:00How to Get Values From Form1 to Form2 (or any other forms, for that matter).This question is asked practically every day on C# Forums: "how do I get a value from Form1 to Form2?" or the other way around. There is a shocking amount of bad advice out there on the internet about how to go about doing this. This tutorial is intended to show you more "correct" ways of passing data between forms.<br />
<br />
The scope of this tutorial is limited to passing data between forms. We will look at both properties and custom events, but in a brief overview.<br />
<a name='more'></a><br />
There are already some excellent tutorials that cover some of these topics in more detail, should you want more information:<br />
<a href="http://www.dreamincode.net/forums/topic/176796-quick-and-easy-custom-events/">Quick and easy custom events</a><br />
<a href="http://www.dreamincode.net/forums/topic/176782-building-an-application-poscash-register-part-one/">Bulding an application - Part 1</a><br />
<a href="http://www.dreamincode.net/forums/topic/176791-building-an-application-poscash-register-part-two/">Building an application - Part 2</a><br />
<br />
Let's start with a new Windows Forms project (though note, these methods can also be used for WPF forms as well). I'm creating three forms: <span style="background-color: lightgrey;">DefaultForm</span>, <span style="background-color: lightgrey;">FirstNameForm</span>, and <span style="background-color: lightgrey;">LastNameForm</span>. DefaultForm is "Form1", the Form that is opened when the application starts.<br />
<br />
There are two types of child form: <a href="http://en.wikipedia.org/wiki/Modal_window">modal</a> and <a href="http://en.wikipedia.org/wiki/Dialog_box#Modeless">modeless (non-modal)</a>. There are different techniques for getting information from both, since they behave differently. <br />
<br />
Modeless child windows do not require an immediate response, and do not block you from focusing the parent form again. You are allowed to freely switch back and forth between them. An example of a modeless dialog would be the "Find" (ctrl+F) dialog in Visual Studio. <br />
<br />
Modal dialogs, on the other hand, block you from switching back to the parent window. They require a response before continuing. An example of a modal dialog is a MessageBox.<br />
<br />
Depending on which type of form you choose to use, you will need to get the data from it differently.<br />
<br />
<u><b>Modeless</b></u><br />
<br />
First, we'll visit modeless forms. A form is treated as modeless when it is opened by the <a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.control.show.aspx">.Show</a> method. Execution does not stop on that line; the code after it continues to execute even though the child form is now shown and focused.<br />
<br />
<i>We'll use <span style="background-color: lightgrey;">FirstNameForm</span> as the modeless dialog.</i><br />
<br />
Since execution does not stop, we need a way to know when the value we want is updated. <a href="http://msdn.microsoft.com/en-us/library/awbftdfh.aspx">Custom events</a> are the answer. The reason we like to use events rather than just passing properties around is that we don't want to tightly couple our forms. DefaultForm doesn't need to know anything more about FirstNameForm than it should. This way, it doesn't have to know anything about it other than that it has one event. It doesn't need to know names of properties, and it doesn't have to guess about when the properties are updated.<br />
<br />
Another benefit is reusability. You may want several forms to update themselves when a child window does. This way, each form can subscribe to the event and handle it the way they choose. As another expert on DIC explains, it's like bingo. When a number is drawn, the guy doesn't go down the aisle and mark each person's board. He calls it out (an event) and each person handles it accordingly (the subscribers handling the event).<br />
<br />
Let's create a custom EventArgs class to suit our needs. This simple class extends EventArgs, and has a public string property called Name.<br />
<pre class="brush: c-sharp">public class NameUpdatedEventArgs : EventArgs
{
public string Name { get; set; }
}</pre><br />
We'll use that EventArgs class to pass our value with. Now, we must create the event itself. Inside the class definition for FirstNameForm, we'll add the folowing code:<br />
<pre class="brush: c-sharp">public event EventHandler<NameUpdatedEventArgs> FirstNameUpdated;
protected virtual void OnFirstNameUpdated(NameUpdatedEventArgs e)
{
if (FirstNameUpdated != null)
FirstNameUpdated(this, e);
}</pre><br />
The first line creates the event itself. The method that follows actually triggers the event itself. It expects you to pass it a NameUpdatedEventArgs, which it will use as one of the event's parameters. So when we want our FirstNameUpdated event to be triggered, we call our OnFirstNameUpdated method.<br />
<br />
Now that we have the code for an event up, we need to add the logic to call that event. In my example, we're going to update the value when the user clicks a button:<br />
<pre class="brush: c-sharp">private void UpdateButton_Click(object sender, EventArgs e)
{
string newFirstName = firstNameTextBox.Text;
NameUpdatedEventArgs nuea = new NameUpdatedEventArgs();
nuea.Name = newFirstName;
OnFirstNameUpdated(nuea);
}</pre><br />
First, we're getting the new first name. Then, we create a new NameUpdateEventArgs object. Then, we assign the new first name to the Name property of the args. Lastly, we call the method that triggers the FirstNameUpdated event.<br />
<br />
Now we're done with FirstNameForm. It's set up to broadcast its event, and needs nothing else to work.<br />
<br />
In the DefaultForm, we need to handle the event. In this example, we are going to open the child window as soon as we open the DefaultForm, so we'll create and show the FirstNameForm in DefaultForm's constructor:<br />
<pre class="brush: c-sharp">public DefaultForm()
{
InitializeComponent();
FirstNameForm fnf = new FirstNameForm();
fnf.FirstNameUpdated += new EventHandler<NameUpdatedEventArgs>(fnf_FirstNameUpdated);
fnf.Show();
}</pre><br />
The first line of the method is the standard method call that Visual Studio adds to every form to set up all the GUI components. The next line creates a new instance of FirstNameForm. <br />
The next line adds an event handler**. And the last shows the window in a modeless way.<br />
<br />
**A quick note on adding event handlers: Visual Studio will do most of the work for you. Type the beginning of the line up to the +=:<br />
<br />
<img src="http://i.imgur.com/sDEK9.png/" /><br />
<br />
And Visual Studio asks you to push TAB. Push it:<br />
<br />
<img src="http://i.imgur.com/SpHmj.png/" /><br />
<br />
And it finishes the line for you. But it wants you to push tab again. Do it:<br />
<br />
<img src="http://i.imgur.com/ZBMkI.png/" /><br />
<br />
And it creates the handler method for you as well. It really does take the guesswork and memorization out of making event handlers.<br />
<br />
That method that VS created for us will be the code that executes every time that FirstNameUpdated is triggered. Let's fill it in:<br />
<br />
<pre class="brush: c-sharp">void fnf_FirstNameUpdated(object sender, NameUpdatedEventArgs e)
{
if (e != null && e.Name != null)
firstNameLabel.Text = e.Name;
}</pre><br />
Remember that we added the name to our NameUpdatedEventArgs? That's in the variable <span style="background-color: lightgrey;">e</span>. First we check to make sure e and it's property are not null, then we take the Name value from e and assign it to a label.<br />
<br />
And that's that. When we run the program, we see this before the update:<br />
<br />
<img src="http://i.imgur.com/3Q1LW.png/" /><br />
<br />
And after update:<br />
<br />
<img src="http://i.imgur.com/oLT0r.png/" /><br />
<br />
The FirstNameDialog will not go away until you close it.<br />
<br />
<u><b>Modal</b></u><br />
<br />
Next, we'll visit modeless forms. A form is treated as moda; when it is opened by the <a href="http://msdn.microsoft.com/en-us/library/system.windows.forms.form.showdialog.aspx">.ShowDialog</a> method. Execution does stop at that line until the child form is closed. The ShowDialog method returns a DialogResult object.<br />
<br />
<i>We'll use <span style="background-color: lightgrey;">LastNameForm</span> as the modal dialog.</i><br />
<br />
Now, modal dialogs are a bit different of a model from the other types. They're usually more limited in their focus and purpose, and execution of the parent form is blocked while they're open,\ so it's OK to tightly couple them with a parent form. To an extent. Neither form should know about the UI controls on the other, of course. That's some of the worst advice that's common on the web "make your TextBox public." <b>Don't do that!</b>. Instead, wrap your TextBox's Text property in a public Property. Here's how we would do this in LastNameForm (this would be added inside the class LastNameForm):<br />
<br />
<pre class="brush: c-sharp">public string LastName
{
get
{
return lastNameTextBox.Text;
}
}</pre><br />
Now instead of dealing with an entire TextBox, we're just dealing with a string.<br />
<br />
Now, since we don't need a way to know when the child form is closed (because ShowDialog is a blocking call), we just need to make sure to close the form when we get what we want. In this example, I'm closing the form when someone clicks a button labeled "OK":<br />
<br />
<pre class="brush: c-sharp">private void OkButton_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.OK;
this.Close();
}</pre><br />
We're setting the DialogResult to OK to let the other form know it wasn't forced closed or cancelled.<br />
<br />
And that's all we have to do for LastNameDialog. Now we need to look at creating the dialog and retrieving it's value.<br />
<br />
In DefaultForm, we'll launch a LastNameDialog when a user clicks a button:<br />
<br />
<pre class="brush: c-sharp">private void GetLastNameButton_Click(object sender, EventArgs e)
{
LastNameForm lnf = new LastNameForm();
DialogResult res = lnf.ShowDialog();
if (res == DialogResult.OK)
lastNameLabel.Text = lnf.LastName;
}</pre><br />
The first line creates a new LastNameForm. The next calls it as a Modal dialog, and retrieves its response once it's closed. The next lines test to see if the DialogResult was "OK", and if so, uses the value of the LastName property for a label on the DefaultForm.<br />
<br />
And that's really all there is when it comes to modal dialogs. Remember, this way is less involved, but Modal dialogs block execution of the main form, and you can't switch from one to the other. So you must make your choice of which to use wisely.<br />
<br />
Here's how it looks when using it. Before clicking OK:<br />
<br />
<img src="http://i.imgur.com/UHflz.png/" /><br />
<br />
After clicking OK:<br />
<br />
<img src="http://i.imgur.com/oLT0r.png/" /><br />
<br />
Note that the dialog went away when we clicked OK.<br />
<br />
<u><b>Passing a value to a new Form</b></u><br />
<br />
Another very common related question involves passing values to a <i>new</i> form. One very easy way to do this is to remember that Forms are just Classes, and that all classes have constructors. Calling <span style="background-color: lightgrey;">new Form1()</span> is really no different than <span style="background-color: lightgrey;">new Random()</span> or any other class. And because this form is a class you are creating, you have control over the constructor, and its parameters.<br />
<br />
I'll continue on with the previous example. We'll add a form called <span style="background-color: lightgrey;">DisplayNameForm</span>. This form is simply a display form, not an input form, so we don't have to worry about events or anything like that. But we need it to display the name, so it needs both names when it's created. Normally, when a form is created, it's created with a default constructor:<br />
<br />
<pre class="brush: c-sharp">public DisplayNameForm()
{
InitializeComponent();
}</pre><br />
But just because it starts that way doesn't mean it has to stay that way. Let's add some parameters to the constructor, and some assignment logic.<br />
<br />
<pre class="brush: c-sharp">public DisplayNameForm(string firstName, string lastName)
{
InitializeComponent();
firstNameLabel.Text = firstName;
lastNameLabel.Text = lastName;
}</pre><br />
Now the form requires both a first name and a last name to be created. And when it's created, it assigns those values to some labels on the form.<br />
<br />
This, of course, changes the way we have to create and call the form. For this example, in DisplayForm, I'm showing this DisplayNameForm on a button click:<br />
<br />
<pre class="brush: c-sharp">private void DisplayNameButton_Click(object sender, EventArgs e)
{
string firstName = firstNameLabel.Text;
string lastName = lastNameLabel.Text;
DisplayNameForm dnf = new DisplayNameForm(firstName, lastName);
dnf.Show();
}</pre><br />
The first two lines gather the names. The third is the actual creation of the new form. Notice that we're providing the names as parameters to the constructor. They match the parameters in the constructor's definition. Lastly, we show it like any other form.<br />
<br />
<u><b>Conclusion</b></u><br />
<br />
I hope you have enjoyed this tutorial. I've tried to make it as simple as possible while still following sound design and object oriented principles.<br />
<br />
This article was crossposted from <a href="http://www.dreamincode.net/forums/topic/206458-how-to-get-values-from-form1-to-form2-or-any-other-forms/">DreamInCode.Net</a> and <a href="http://bytes.com/topic/c-sharp/insights/904861-how-get-data-form1-form2-any-other-forms#post3637458">Bytes.Com</a>.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com2tag:blogger.com,1999:blog-5956282563643600179.post-76704155895292810932010-11-26T13:54:00.003-06:002010-11-26T14:22:13.962-06:00Silverlight RPN Calculator Tutorial.Well I beat my old nemesis and finished an RPN calculator. It was much easier than I remember, but then again, I tried it like four years ago. I've learned so much in the time since, I can hardly believe how silly my old code is.<br />
<br />
Have any of you ever used a <a href="http://en.wikipedia.org/wiki/Reverse_Polish_notation">Reverse Polish Notation</a> calculator? I did in high school. It was easily the best calculator ever (the HP=32SII). RPN is great, because you don't have to use parenthesis. The stack and the order in which you key the operators maintains order of operations.<br />
<br />
Today, I'll walk you through creating one. Note this tutorial is technically for Silverlight, but except for the marked sections, it can be applied wholesale to WinForms or WPF. Most if this is simple stack logic, and that exists on all .NET platforms.<br />
<br />
Read more after the jump.<br />
<a name='more'></a><br />
<i>Note: I'm assuming you are familiar with <a href="http://msdn.microsoft.com/en-us/library/system.collections.stack.aspx">stack</a> operations. If not, please visit the linked MSDN page for information.</i><br />
<br />
Before we start programming, you have to understand postfix notation. Consider the following:<br />
<br />
<pre class="brush: c-sharp">5 6 +</pre><br />
This is a postfix expression, equating to 11. The logical steps to evaluate it are:<br />
<ol><li>Push(5)</li>
<li>Push(6)</li>
<li>var b = Pop()</li>
<li>var a = Pop()</li>
<li>Push(a + b)</li>
<li>Peek()</li>
</ol><br />
Consider a more complicated expression:<br />
<br />
<pre class="brush: c-sharp">10 4 6 + 9 * -</pre><br />
This evaluates to -80. It is the equivelant of 10 - (( 4 + 6 ) * 9) in Infix (standard) notation. The same basic steps are followed here.<br />
<ol><li>Push(10)</li>
<li>Push(4)</li>
<li>Push(6)</li>
<li>var b = Pop()</li>
<li>var a = Pop()</li>
<li>Push(a + b)</li>
<li>Push(9)</li>
<li>var b = Pop()</li>
<li>var a = Pop()</li>
<li>Push(a * b)</li>
<li>var b = Pop()</li>
<li>var a = Pop()</li>
<li>Push(a - b)</li>
<li>Peek()</li>
</ol><br />
The basic logic is to push numbers onto the stack until you encounter an operator. At that point, you pop the top two off the stack, evaluate them with the operator, and push the result back onto the stack. After evaluation, Peek for display.<br />
<br />
In RPN calculator terms, there is an ENTER button to push a value onto the stack. Also, to save time, if you're currently entering a number, pushing an operator button will also push the number onto the stack.<br />
<br />
So for <span style="background-color: lightgrey;">5 6 + 3 *</span>, we'd push:<br />
5 [ENTER] 6 [+] 3 [*].<br />
<br />
For <span style="background-color: lightgrey;">1 6 2 / 3 4 / * +</span> (which is the equivalent of <span style="background-color: lightgrey;">1 + (( 6 / 2 ) * ( 3 / 4 ))</span>, you'd push:<br />
1 [ENTER] 6 [ENTER] 2 [/] 3 [ENTER] 4 [/] [*] [+]<br />
<br />
On a standard calculator, you'd have to use the Memory function to do this with the proper order of operation. With a graphing calculator, you'd have to use parenthesis. But since you can keep state in the stack, you don't have to worry about any of that with an RPN calc.<br />
<br />
So, without further ado, lets get into the code.<br />
<br />
<b>Setup</b><br />
<br />
We'll obviously need a stack:<br />
<br />
<pre class="brush: c-sharp">private Stack<double> stack;</pre><br />
We'll also need some Dictionaries to relate keyboard keys with strings, since we're not trusting the users to enter numbers by themselves:<br />
<br />
<pre class="brush: c-sharp">private Dictionary<Key, string> opKeys = new Dictionary<Key, string>();
private Dictionary<Key, string> numKeys = new Dictionary<Key, string>();
private void InitializeDictionaries()
{
opKeys.Add(Key.C, "C");
opKeys.Add(Key.Back, "B");
opKeys.Add(Key.Add, "+");
opKeys.Add(Key.Subtract, "-");
opKeys.Add(Key.Multiply, "*");
opKeys.Add(Key.Divide, "/");
opKeys.Add(Key.Enter, "E");
opKeys.Add(Key.Decimal, ".");
numKeys.Add(Key.D0, "0");
numKeys.Add(Key.D1, "1");
numKeys.Add(Key.D2, "2");
numKeys.Add(Key.D3, "3");
numKeys.Add(Key.D4, "4");
numKeys.Add(Key.D5, "5");
numKeys.Add(Key.D6, "6");
numKeys.Add(Key.D7, "7");
numKeys.Add(Key.D8, "8");
numKeys.Add(Key.D9, "9");
numKeys.Add(Key.NumPad0, "0");
numKeys.Add(Key.NumPad1, "1");
numKeys.Add(Key.NumPad2, "2");
numKeys.Add(Key.NumPad3, "3");
numKeys.Add(Key.NumPad4, "4");
numKeys.Add(Key.NumPad5, "5");
numKeys.Add(Key.NumPad6, "6");
numKeys.Add(Key.NumPad7, "7");
numKeys.Add(Key.NumPad8, "8");
numKeys.Add(Key.NumPad9, "9");
}</pre><br />
Now, we're going to use Lambda methods for the operators. This makes things quite simple. If you're not familiar with what's going on here, I have a <a href="http://www.dreamincode.net/forums/topic/177103-lambdas-the-operator-what-are-they-and-how-are-they-used/">tutorial on Lambdas</a> that you're welcome to read. They're basically a shorthand way to write anonymous methods. And the Func object is a way to store these methods as objects. We're going to make a Dictionary of Funcs keyed by strings:<br />
<br />
<pre class="brush: c-sharp">private Dictionary<string, Func<double, double, double>> op =
new Dictionary<string, Func<double, double, double>>();
private void InitializeOp()
{
op.Add("+", (a, B) => a + B);
op.Add("-", (a, B) => a - B);
op.Add("*", (a, B) => a * B);
op.Add("/", (a, B) => a / B);
}</pre><br />
If this seems strange to you, here's an example of how we would invoke this.<br />
<br />
<b>This doesn't go in the project. This is just an example of using Funcs in a dictionary.</b><br />
<pre class="brush: c-sharp">double a = 5, b = 6;
double result = op["+"](5, 6);</pre><br />
Now we've stored methods in a dictionary, and depending on which operator we pass, the proper one will be invoked. This is quite useful, because it allows us to skip a switch or if else if statement.<br />
<br />
Also, two more bools we'll need to keep track of state:<br />
<br />
<pre class="brush: c-sharp">private bool clearOnNext, errorState;</pre><br />
clearOnNext lets us know if the next number we push will start a new number or append to the current one. errorState is a simple bool that lets us know if we're currently reporting an error to the user (like Div By Zero or Out of Stack).<br />
<br />
<b>Silverlight Specific: XAML MARKUP</b> (display code)<br />
<br />
Here's the XAML markup we're using. This could be replicated by WinForms, but I prefer to work with Silverlight/WPF whenever I can.<br />
<br />
<pre class="brush: xml"><grid background="White" x:name="LayoutRoot">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Max length: 15 -->
<Grid Grid.Row="0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="30" />
<ColumnDefinition Width="50" />
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border BorderBrush="Black" BorderThickness="1" CornerRadius="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock Text="Stack Depth" Grid.Row="0" FontSize="8" TextWrapping="Wrap" VerticalAlignment="Bottom" HorizontalAlignment="Center" />
<TextBlock Text="0" Grid.Row="1" VerticalAlignment="Bottom" HorizontalAlignment="Center" x:Name="stackDepthTextBlock" />
</Grid>
</Border>
<Border Grid.Column="1" BorderBrush="Black" BorderThickness="1" CornerRadius="3">
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Button x:Name="copyButton" Content="Copy" Click="copyButton_Click" />
<Button x:Name="pasteButton" Content="Paste" Grid.Row="1" Click="pasteButton_Click" />
</Grid>
</Border>
<TextBox IsReadOnly="True" x:Name="displayTextBox" Text=""
Grid.Column="2" Grid.Row="0" Grid.RowSpan="2" HorizontalAlignment="Stretch" TextAlignment="Right"
FontFamily="Courier New" FontSize="26" FontWeight="ExtraBold"
KeyUp="DisplayTextBox_KeyUp" />
</Grid>
<Grid Grid.Row="1">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- Buttons -->
<Button Content="Enter" Tag="E" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Click="Button_Click" IsTabStop="False" />
<Button Content="C" Tag="C" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="0" Grid.Column="2" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="?" Tag="B" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="0" Grid.Column="3" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="7" Tag="7" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="8" Tag="8" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="9" Tag="9" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="1" Grid.Column="2" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="÷" Tag="/" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="1" Grid.Column="3" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="4" Tag="7" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="1" Click="Button_Click" />
<Button Content="5" Tag="8" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="1" Click="Button_Click" />
<Button Content="6" Tag="9" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="1" Click="Button_Click" />
<Button Content="×" Tag="*" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="1" Click="Button_Click" />
<Button Content="4" Tag="4" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="5" Tag="5" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="2" Grid.Column="1" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="6" Tag="6" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="2" Grid.Column="2" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="×" Tag="*" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="2" Grid.Column="3" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="1" Tag="1" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="2" Tag="2" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="3" Grid.Column="1" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="3" Tag="3" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="3" Grid.Column="2" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="-" Tag="-" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="3" Grid.Column="3" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="0" Tag="0" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="2" Click="Button_Click" IsTabStop="False" />
<Button Content="." Tag="." Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="4" Grid.Column="2" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<Button Content="+" Tag="+" Style="{StaticResource ViewboxButton}" Margin="2" Grid.Row="4" Grid.Column="3" Grid.ColumnSpan="1" Click="Button_Click" IsTabStop="False" />
<!-- /Buttons -->
</Grid>
</Grid>
</Grid></grid></pre><br />
One neat thing I did was to make a new style for a Button. I copied the Button's default template from the MSDN, but the one small change I made was putting the <contentpresenter> inside a <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.viewbox.aspx"><viewbox></viewbox></a>. The Viewbox is a neat control that will stretch and scale a single child to fill all available space. Without it, the buttons can scale to fit, but the text inside would remain the same size. With it, the text scales to fill the button. Here's the template.<br />
<br />
</contentpresenter><br />
<pre class="brush: xml"><Style x:Key="ViewboxButton" TargetType="Button">
<Setter Property="Background" Value="#FF1F3B53"/>
<Setter Property="Foreground" Value="#FF000000"/>
<Setter Property="Padding" Value="3"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FFA3AEB9" Offset="0"/>
<GradientStop Color="#FF8399A9" Offset="0.375"/>
<GradientStop Color="#FF718597" Offset="0.375"/>
<GradientStop Color="#FF617584" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<vsm:VisualStateManager.VisualStateGroups>
<vsm:VisualStateGroup x:Name="CommonStates">
<vsm:VisualState x:Name="Normal"/>
<vsm:VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundAnimation" Storyboard.TargetProperty="Opacity" To="1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="#F2FFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#CCFFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#7FFFFFFF"/>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Pressed">
<Storyboard>
<ColorAnimation Duration="0" Storyboard.TargetName="Background" Storyboard.TargetProperty="(Border.Background).(SolidColorBrush.Color)" To="#FF6DBDD1"/>
<DoubleAnimation Duration="0" Storyboard.TargetName="BackgroundAnimation" Storyboard.TargetProperty="Opacity" To="1"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[0].(GradientStop.Color)" To="#D8FFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[1].(GradientStop.Color)" To="#C6FFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[2].(GradientStop.Color)" To="#8CFFFFFF"/>
<ColorAnimation Duration="0" Storyboard.TargetName="BackgroundGradient" Storyboard.TargetProperty="(Rectangle.Fill).(GradientBrush.GradientStops)[3].(GradientStop.Color)" To="#3FFFFFFF"/>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="DisabledVisualElement" Storyboard.TargetProperty="Opacity" To=".55"/>
</Storyboard>
</vsm:VisualState>
</vsm:VisualStateGroup>
<vsm:VisualStateGroup x:Name="FocusStates">
<vsm:VisualState x:Name="Focused">
<Storyboard>
<DoubleAnimation Duration="0" Storyboard.TargetName="FocusVisualElement" Storyboard.TargetProperty="Opacity" To="1"/>
</Storyboard>
</vsm:VisualState>
<vsm:VisualState x:Name="Unfocused" />
</vsm:VisualStateGroup>
</vsm:VisualStateManager.VisualStateGroups>
<Border x:Name="Background" CornerRadius="3" Background="White" BorderThickness="{TemplateBinding BorderThickness}" BorderBrush="{TemplateBinding BorderBrush}">
<Grid Background="{TemplateBinding Background}" Margin="1">
<Border Opacity="0" x:Name="BackgroundAnimation" Background="#FF448DCA" />
<Rectangle x:Name="BackgroundGradient" >
<Rectangle.Fill>
<LinearGradientBrush StartPoint=".7,0" EndPoint=".7,1">
<GradientStop Color="#FFFFFFFF" Offset="0" />
<GradientStop Color="#F9FFFFFF" Offset="0.375" />
<GradientStop Color="#E5FFFFFF" Offset="0.625" />
<GradientStop Color="#C6FFFFFF" Offset="1" />
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
</Grid>
</Border>
<Viewbox>
<ContentPresenter Content="{TemplateBinding Content}" />
</Viewbox>
<Rectangle x:Name="DisabledVisualElement" RadiusX="3" RadiusY="3" Fill="#FFFFFFFF" Opacity="0" IsHitTestVisible="false" />
<Rectangle x:Name="FocusVisualElement" RadiusX="2" RadiusY="2" Margin="1" Stroke="#FF6DBDD1" StrokeThickness="1" Opacity="0" IsHitTestVisible="false" />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style></pre><br />
<b>Non-Silverlight Specific</b><br />
<br />
If you're not doing this in Silverlight, the important thing to note for the rest of this tutorial is that I'm assuming you created a Read Only TextBox named "displayTextBox". You can name it whatever you like, just make sure to change it in the code as well. Another thing I've assumed is that you've created all your buttons, and set their Tag properties to their numeral or operator value. Also, they all should call the same handler for their Click event: "Button_Click". One more thing, I've attached an event handler to the KeyUp event of the displayTextBox.<br />
<br />
Here's a property we'll be using for convenience sake:<br />
<br />
<pre class="brush: c-sharp">private double current
{
get
{
if (displayTextBox.Text == string.Empty)
displayTextBox.Text = "0";
return double.Parse(displayTextBox.Text);
}
}</pre><br />
This simply parses what's in the TextBox as a double.<br />
<br />
<b>Initialization</b><br />
<br />
Here's how our constructor will look:<br />
<br />
<pre class="brush: c-sharp">public MainPage()
{
InitializeComponent();
InitializeDictionaries();
InitializeOp();
stack = new Stack<double>();
this.Loaded += (s, ea) =>
{
if (!App.Current.IsRunningOutOfBrowser)
System.Windows.Browser.HtmlPage.Plugin.Focus();
displayTextBox.Focus();
};
clearOnNext = false;
errorState = false;
}</pre><br />
There's a lambda in here too. It's just a quick event handler to set the focus onto the Display TextBox when the program loads. Since for some reason, the Silverlight app doesn't focus itself when it starts, I've added a line to tell the browser to first focus the SL app first.<br />
<br />
<b>Methods</b><br />
<br />
The simplest method we need to add has to handle input. We'll call this one from handlers.<br />
<br />
<pre class="brush: c-sharp">private void ProcessInput(string input)
{
if (clearOnNext)
{
displayTextBox.Text = string.Empty;
clearOnNext = false;
errorState = false;
}
displayTextBox.Text += input;
}</pre><br />
It checks to see if we're starting a new number. If so, it clears it and resets the state, otherwise, it just appends text.<br />
<br />
We'll also need a method to process special characters like "Clear", "Enter", "Backspace", and "Decimal" as well as the operators<br />
<br />
<pre class="brush: c-sharp">private void ProcessSpecial(string input)
{
switch (input)
{
case "C":
if (displayTextBox.Text.Length > 0)
displayTextBox.Text = string.Empty;
else
{
stack.Clear();
RefreshDepthText();
}
clearOnNext = false;
break;
case "B":
if (!clearOnNext && displayTextBox.Text.Length > 0)
displayTextBox.Text = displayTextBox.Text.Substring(0, displayTextBox.Text.Length - 1);
break;
case ".":
if (!displayTextBox.Text.Contains("."))
{
if (displayTextBox.Text.Length < 1 || errorState)
ProcessInput("0.");
else
ProcessInput(".");
}
break;
case "E":
Enter();
break;
case "+":
case "-":
case "*":
case "/":
DoOp(input);
break;
}
}</pre><br />
This one walks through the possibilities in a switch statement and takes the appropriate action. At this point, some of the methods defined here are undeclared. We'll soon remedy this. I'll also explain each case.<br />
<br />
Case "C" is clear. First click, it clears the display. Second (or first if the display is already clear) clears the stack.<br />
<br />
Case "B" is backspace. Substring if there's room to.<br />
<br />
Case "." handles the decimal. We only allow one of those, and for visual's sake, add a 0 in front if it's the first button clicked.<br />
<br />
Case "E" is enter. We'll write that method soon.<br />
<br />
The remaining cases are operators. We'll also look at that method soon.<br />
<br />
<b>Calculator Logic methods</b><br />
<br />
Here's the Enter method now:<br />
<br />
<pre class="brush: c-sharp">private void Enter()
{
if (!errorState)
{
stack.Push(current);
RefreshDepthText();
clearOnNext = true;
}
}</pre><br />
It's quite simple. We don't want to try to push an error message on the stack, so we check for that first. If there's no error, we push the current value onto the stack, and refresh the Depth Text Display (this just shows a count of how much is on the stack). Also, we set clearOnNext to true, since we'll be starting a new number.<br />
<br />
<br />
Here's the method where we actually do an operation:<br />
<br />
<pre class="brush: c-sharp">private void DoOp(string input)
{
if (!clearOnNext)
{
stack.Push(current);
}
if (stack.Count < 2)
{
errorState = true;
clearOnNext = true;
displayTextBox.Text = "OUT OF STACK";
return;
}
double b = stack.Pop();
double a = stack.Pop();
stack.Push(op[input](a, B));
double res = stack.Peek();
if (res == double.NegativeInfinity || res == double.PositiveInfinity || res == double.NaN)
{
stack.Clear();
RefreshDepthText();
errorState = true;
clearOnNext = true;
displayTextBox.Text = "DIV BY ZERO";
return;
}
displayTextBox.Text = stack.Peek().ToString();
RefreshDepthText();
clearOnNext = true;
}</pre><br />
The first if statement checks to see if we've been entering a new number. Remember, the way an RPN calc works is that if you're entering a number, an operator key will push that number for you. Next, we check to see if we can pop enough values to proceed. If you try to do an operation that there's not enough numbers for, you'll "run out of stack." Once we know we can, we pop both values, b first (this only matters for subtraction and division). Now we perform the actual operation. My first thought was that we could use a Try/Catch to check for Div By Zero, but apparently that results in double.PositiveInfinity. The string "Infinity" is also a valid parsable double, by the way. So I'm just checking the result now.<br />
<br />
Anyway, if everything was successful, Peek the value into the textbox, and move on.<br />
<br />
Now, a keen observer would note that while we're set up to handle the calculator logic now, we have no way of actually entering numbers or operators. Have no fear, that's the next section.<br />
<br />
<b>Input logic</b><br />
<br />
We've set up event handlers for the KeyUp on the readonly textbox as well as handling the Click event for each button. Here's the code for those event handlers:<br />
<br />
<pre class="brush: c-sharp">private void Button_Click(object sender, RoutedEventArgs e)
{
string value = (sender as Button).Tag as string;
if (numKeys.ContainsValue(value))
ProcessInput(value);
else
ProcessSpecial(value);
displayTextBox.Focus();
}
private void DisplayTextBox_KeyUp(object sender, KeyEventArgs e)
{
if (opKeys.ContainsKey(e.Key))
ProcessSpecial(opKeys[e.Key]);
else if (numKeys.ContainsKey(e.Key))
ProcessInput(numKeys[e.Key].ToString());
else return;
}</pre><br />
<b>Miscellaneous methods</b><br />
<br />
These don't hold much importance, just necessary for little things:<br />
<br />
<pre class="brush: c-sharp">private void RefreshDepthText()
{
stackDepthTextBlock.Text = stack.Count.ToString();
}
private void copyButton_Click(object sender, RoutedEventArgs e)
{
Clipboard.SetText(current.ToString());
}
private void pasteButton_Click(object sender, RoutedEventArgs e)
{
if (!Clipboard.ContainsText())
return;
string s = Clipboard.GetText();
double x;
if (double.TryParse(s, out x))
displayTextBox.Text = s;
}</pre><br />
And that's that. If you've followed the instructions properly, you'll have a calculator like this one:<br />
<br />
<div class="separator" style="clear: both; text-align: center;"><a href="http://imgur.com/ZWbxt.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://imgur.com/ZWbxt.png" width="231" /></a></div><br />
Cross posted from DreamInCode. Will update when it goes live.<br />
<br />
The sample project will be available on the Dream In Code tutorial page once it's approved, since Blogger apparently won't let me upload it here.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-88309912254319881922010-11-23T17:37:00.005-06:002010-11-26T14:20:59.050-06:00Postfix expression parser.A question on <a href="http://www.dreamincode.net/forums/topic/201613-unit-conversion/page__gopid__1175854&">DreamInCode</a> lead me to my old nemesis, the Postfix expression parser. I'd fallen in love with a <a href="http://en.wikipedia.org/wiki/Reverse_Polish_notation">Reverse Polish Notation</a> calculator in high school (the HP-32SII, they sadly don't make them anymore). When I tried to program a RPNC to use on my PC years ago, I failed miserably. But I've learned a lot since then, and I figured I could at least try a simple postfix parser.<br />
<br />
It wasn't that bad. You don't have to check for a lot of things a calculator does. Here's the body of the code. I made it static, since you really don't need to instantiate it. If I ever make it into a calculator, I'd probably make it non-static, and make the stack a class-level field.<br />
<br />
<pre class="brush: c-sharp">class PostfixParser
{
const string Placeholder = "[x]";
private static Dictionary<string, Func<double, double, double>> op;
private static List<string> operators = new List<string>() { "+", "-", "*", "/" };
static PostfixParser()
{
op = new Dictionary<string, Func<double, double, double>>();
op.Add("+", (x, y) => x + y);
op.Add("-", (x, y) => x - y);
op.Add("*", (x, y) => x * y);
op.Add("/", (x, y) => x / y);
op.Add("^", (x, y) => Math.Pow(x, y));
}
public static double ParseExp(string exp, double? val)
{
string[] tokens = exp.Split(new string[] { " " } , StringSplitOptions.RemoveEmptyEntries);
if (tokens.Contains(Placeholder) && val == null)
throw new InvalidOperationException("Placeholder token detected in expression, but parameter \"val\" was null.");
Stack<double> stack = new Stack<double>();
foreach (string token in tokens)
{
if (operators.Contains(token))
{
if (stack.Count < 2)
throw new InvalidOperationException("Operator imbalance.");
double y = stack.Pop();
double x = stack.Pop();
stack.Push(op[token](x, y));
}
else
{
double n;
if (token == Placeholder)
n = val.Value;
else if (!double.TryParse(token, out n))
throw new InvalidOperationException("Invalid token detected: " + token);
stack.Push(n);
}
}
if (stack.Count > 1)
throw new InvalidOperationException("More than one result on the stack.");
if (stack.Count < 1)
throw new InvalidOperationException("No results on the stack.");
return stack.Pop();
}
public static double ParseExp(string exp)
{
return ParseExp(exp, null);
}
}</pre><br />
This can be used like this:<br />
<pre class="brush: c-sharp">string fToCExp = "5 9 / [x] 32 - *";
string cToFExp = "9 5 / [x] * 32 + ";
Console.WriteLine("100C converted to F:");
Console.WriteLine(PostfixParser.ParseExp(cToFExp, 100));
Console.WriteLine("32F converted to C:");
Console.WriteLine(PostfixParser.ParseExp(fToCExp, 32));
Console.ReadKey();</pre><br />
To give an output of:<br />
<br />
<pre class="consoleOut">100C converted to F:
212
32F converted to C:
0</pre><br />
You could add more operators if you wished, like a 1/x or a SQRT, but since most other operators are unary rather than binary, you'd have to change your popping logic.<br />
<br />
<i>Note:</i> I did it with a placeholder value in mind. I used "[x]" for whatever reason, but it could be anything that won't parse into a double by itself or match one of the operators. You could remove this altogether, if you cared to.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-3268749440963778572010-10-26T17:31:00.001-05:002010-10-26T17:45:04.901-05:00More extension methods: To and In. Also, a static class to make Sequences.It's been quite a while since I posted. I've been quite busy with work. But without further ado, here's a static class I've been working on (code after the break):<br />
<a name='more'></a><br />
<pre class="brush: c-sharp">public static partial class Seq
{
public static IEnumerable<T> Make<T>(params T[] list)
{
foreach (T item in list)
yield return item;
}
public static IEnumerable<int> Make(int from, int to)
{
return Make(from, to, x => x + (from <= to ? 1 : -1));
}
public static IEnumerable<int> Make(int from, int by, int to)
{
return Make(from, to, x => x + by);
}
public static IEnumerable<int> Make(int from, int to, Func<int, int> by)
{
if (from <= to)
{
do
{
yield return from;
from = by.Invoke(from);
} while (from <= to);
}
else
{
do
{
yield return from;
from = by.Invoke(from);
} while (from >= to);
}
}
public static IEnumerable<int> Make(int from, Func<int, int> by)
{
while (true)
{
yield return from;
from = by.Invoke(from);
}
}
public static IEnumerable<int> To(this int i, int to)
{
return Make(i, to);
}
public static IEnumerable<int> To(this int i, int to, Func<int, int> by)
{
return Make(i, to, by);
}
}
</pre><br />
This was somewhat inspired by F#'s Seq and Lists. It's not supposed to match, but I got the idea there. I also used this as an excuse to learn how to use yield return statements.<br />
<br />
This allows you to quickly compose sequences. Right now, all but one are for Int32s. It's a known issue that there's no generic constraint for numeric types, so I can't make a generic out of these. I could theoretically make them Decimals, and just downcast using Cast<t>(), but I don't care for that. So, I made ints, and if I ever need others, I'll copy/paste with doubles/bytes/whatever. The class is partial so it can easily be extended.<br />
<br />
Here's a few more interesting extension methods, ones that I didn't feel that were attached to this class specifically but had external use.<br />
<br />
<pre class="brush: c-sharp">public static partial class Extensions
{
public static bool In<T>(this T i, IEnumerable<T> collection)
{
return collection.Contains(i);
}
public static bool In<T>(this T i, params T[] list)
{
return list.Contains(i);
}
public static void Print<T>(this IEnumerable<T> collection, TextWriter tw = null)
{
if(tw == null)
tw = Console.Out;
tw.Write("{ ");
int max = collection.Count();
for (int i = 0; i < max; i++)
{
tw.Write(collection.ElementAt(i).ToString() + " ");
tw.Write(i == max - 1 ? "}" : ", ");
}
tw.WriteLine();
}
}</pre><br />
In is a simple extension method. It basically wraps and reverses the LINQ extension method Contains.<br />
<br />
So instead of doing collection.Contains(10), we can do 10.In(collection). Kinda Ruby-ish, in my opinion. I like the syntax. It's obvious, and in some contexts, it reads better. Actually, the <a href="http://bytes.com/topic/c-sharp/answers/899236-if-statement-multiple-options">forum thread on bytes</a> that started the discussion that led to these methods has an example of where this scans better:<br />
<br />
<pre class="brush: c-sharp">if(25.In(18, 25)) DoSomething();</pre><br />
Also, I've included a simple Print extension that prints any IEnumerable (though not always well), assuming that the type's ToString is defined properly.<br />
<br />
Here's some example and test code:<br />
<br />
<pre class="brush: c-sharp">class Program
{
static void Main()
{
Console.WriteLine("1.To(10)");
1.To(10).Print();
Console.WriteLine("10.To(1)");
10.To(1).Print();
Console.WriteLine("10.To(10)");
10.To(10).Print();
Console.WriteLine("1.To(10, x => x + 2)");
1.To(10, x => x + 2).Print();
Console.WriteLine("Seq.Make(0, x => x + 5).Take(10)");
Seq.Make(0, x => x + 5).Take(10).Print();
Console.WriteLine("Seq.Make(0, 100, x => x + 3)");
Seq.Make(0, 20, x => x + 3).Print();
Console.WriteLine("Seq.Make(\"abc\", \"def\", \"ghi\")");
Seq.Make("abc", "def", "ghi").Print();
Console.WriteLine("10.In(1.To(10))");
Console.WriteLine(10.In(1.To(10)));
Console.WriteLine("\"test\".In(\"test\", \"ing\")");
Console.WriteLine("test".In("test", "ing"));
Console.ReadKey();
}
}</pre>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-12201665967128997792010-09-01T13:30:00.006-05:002010-09-01T13:40:06.997-05:00Silverlight: Performing Multiple Asynchronous CallsIf you've spent any time with Silverlight, you've probably used it with a Web Service (ASMX or WCF, either one). And the first thing that jumps out at you is the fact that Silverlight does not and in fact cannot generate synchronous methods to call the web methods. In stead of a method called GetData that returns a value, you have a GetDataAsync method and a GetDataCompleted handler, to which the return value will be in the EventArg parameter.<br />
<br />
This is not a bad thing. I don't particularly want a plugin blocking the browser's UI thread with a long-running synchronous call. In fact, I've read (somewhere, can't find the reference) that this is the reason that MS didn't allow Silverlight to do synchronous calls.<br />
<br />
However, there are times when you need to do multiple calls, and then have something happen when they are all done. You can't trust that calls will finish in the order that you start them in, because these calls can run simultaneously. There are a few options to do this. I'll show you two examples. Article continues after the jump.<br />
<a name='more'></a><br />
First, here's the code for a simple example WCF service that we'll be using.<br />
<br />
<pre class="brush: c-sharp">using System.ServiceModel;
using System.ServiceModel.Activation;
namespace AsyncExample.Web
{
[ServiceContract(Namespace = "")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ExampleService
{
[OperationContract]
public string GetSomeData() { return "Some Data"; }
[OperationContract]
public string GetSomeMoreData() { return "Some More Data"; }
}
}
</pre><br />
One way is to "chain" your calls together:<br />
<br />
<pre class="brush: c-sharp">using System;
using System.Windows;
using System.Windows.Controls;
using AsyncExample.ExampleService;
namespace AsyncExample
{
public partial class ChainingExample : UserControl
{
private ExampleServiceClient serviceClient;
private string someData, someMoreData;
public ChainingExample()
{
InitializeComponent();
serviceClient = new ExampleServiceClient();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
serviceClient.GetSomeDataCompleted += new EventHandler<GetSomeDataCompletedEventArgs>(serviceClient_GetSomeDataCompleted);
serviceClient.GetSomeMoreDataCompleted += new EventHandler<GetSomeMoreDataCompletedEventArgs>(serviceClient_GetSomeMoreDataCompleted);
serviceClient.GetSomeDataAsync();
}
private void serviceClient_GetSomeDataCompleted(object sender, GetSomeDataCompletedEventArgs e)
{
someData = e.Result;
serviceClient.GetSomeMoreDataAsync();
}
private void serviceClient_GetSomeMoreDataCompleted(object sender, GetSomeMoreDataCompletedEventArgs e)
{
someMoreData = e.Result;
DisplayData();
}
private void DisplayData()
{
string message = "Got all data.\n{0}\n{1}";
MessageBox.Show(string.Format(message, someData, someMoreData));
}
}
}
</pre><br />
This isn't my favorite method, but it's ok for a small number of calls. The reason I don't care for it is because flow doesn't seem intuitive or natural to me. Also, if you add more calls, you have to make changes to both the service call behind and ahead of the ones you add.<br />
<br />
This option has a strength which is also its weakness: it mimics a synchronous pattern. Call 2 is not started until Call 1 is finished. If you have things that <i>must</i> happen in a specific order, this is is a great way to ensure that. However, if you have long running calls (or more than a few short calls) that <i>could</i> be made simultaneously, you hurt performance by using this pattern.<br />
<br />
My favorite way of doing this is to create a "Facts" class that contains the service proxy, public properties for the Result values of the methods, and an event.<br />
<br />
<pre class="brush: c-sharp">using System;
using AsyncExample.ExampleService;
namespace AsyncExample
{
public delegate void GetAllDataCompletedHandler(object sender, EventArgs e);
public class ExampleServiceFacts
{
public event GetAllDataCompletedHandler GetAllDataCompleted;
protected virtual void OnGetAllDataCompleted(EventArgs e)
{
GetAllDataCompleted(this, e);
}
public string SomeData { get; set; }
public string SomeMoreData { get; set; }
private object locker = new object();
private ExampleServiceClient serviceClient;
private int serviceCallsCompletedCount;
private const int totalServiceCalls = 2;
public ExampleServiceFacts()
{
serviceClient = new ExampleServiceClient();
serviceClient.GetSomeDataCompleted += new EventHandler<GetSomeDataCompletedEventArgs>(serviceClient_GetSomeDataCompleted);
serviceClient.GetSomeMoreDataCompleted += new EventHandler<GetSomeMoreDataCompletedEventArgs>(serviceClient_GetSomeMoreDataCompleted);
}
public void GetAllDataAsync()
{
serviceCallsCompletedCount = 0;
serviceClient.GetSomeDataAsync();
serviceClient.GetSomeMoreDataAsync();
}
public void ServiceCallCompleted()
{
lock (locker)
++serviceCallsCompletedCount;
if (serviceCallsCompletedCount >= totalServiceCalls)
OnGetAllDataCompleted(new EventArgs());
}
private void serviceClient_GetSomeDataCompleted(object sender, GetSomeDataCompletedEventArgs e)
{
SomeData = e.Result;
ServiceCallCompleted();
}
private void serviceClient_GetSomeMoreDataCompleted(object sender, GetSomeMoreDataCompletedEventArgs e)
{
SomeMoreData = e.Result;
ServiceCallCompleted();
}
}
}
</pre><br />
With this class, you make one single call (GetAllDataAsync). Internally, this will kick off all your service calls. As each one completes, it iterates a counter (in a threadsafe manner, that's why we lock the section there) and checks to see if it was the last one finished. If not, it does nothing. If it was the last method to finish, it triggers the Completed event that, hopefully, you have subscribed to in your client code:<br />
<br />
<pre class="brush: c-sharp">using System.Windows;
using System.Windows.Controls;
namespace AsyncExample
{
public partial class MainPage : UserControl
{
public MainPage()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
ExampleServiceFacts facts = new ExampleServiceFacts();
string message = "Got all data.\n{0}\n{1}";
facts.GetAllDataCompleted += (snd, ea)
=> MessageBox.Show(string.Format(message, facts.SomeData, facts.SomeMoreData));
facts.GetAllDataAsync();
}
}
}
</pre><br />
Quick note, I've used a Lambda here as the handler for the GetAllDataCompleted event instead of a named method. The idea is the same though.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com3tag:blogger.com,1999:blog-5956282563643600179.post-4827629879770691432010-08-30T16:06:00.002-05:002010-11-24T10:09:56.494-06:00Trimming/rounding a date time. Floors and ceilings.Well, first, credit where credit is due. I found this on <a href="http://stackoverflow.com/questions/1393696/c-rounding-datetime-objects">StackOverflow</a> and only slightly modified it. But it's too good not to share.<br />
<br />
This code will let you "round" a date time to any given interval. You can get the floor or ceiling, or do a traditional round.<br />
<br />
Here's the code:<br />
<br />
<pre class="brush: c-sharp">public static class Extensions
{
public static DateTime Floor(this DateTime date, TimeSpan span)
{
if (span == TimeSpan.FromMinutes(0))
return date;
long ticks = date.Ticks / span.Ticks;
return new DateTime(ticks * span.Ticks);
}
public static DateTime Ceil(this DateTime date, TimeSpan span)
{
if (span == TimeSpan.FromMinutes(0))
return date;
long ticks = (date.Ticks + span.Ticks - 1) / span.Ticks;
return new DateTime(ticks * span.Ticks);
}
public static DateTime Round(this DateTime date, TimeSpan span)
{
if (span == TimeSpan.FromMinutes(0))
return date;
long ticks = (date.Ticks + (span.Ticks / 2)) / span.Ticks;
return new DateTime(ticks * span.Ticks);
}
}
</pre><br />
Here's a simple example using Floor:<br />
<pre class="brush: c-sharp">DateTime dt = new DateTime(2010, 8, 29, 15, 26, 23);
Console.WriteLine("Floor:");
Console.WriteLine(dt.Floor(TimeSpan.FromSeconds(0)));
Console.WriteLine(dt.Floor(TimeSpan.FromMinutes(1)));
Console.WriteLine(dt.Floor(TimeSpan.FromMinutes(5)));
Console.WriteLine(dt.Floor(TimeSpan.FromMinutes(10)));
Console.WriteLine(dt.Floor(TimeSpan.FromMinutes(60)));
Console.WriteLine();
Console.WriteLine("Ceil:");
Console.WriteLine(dt.Ceil(TimeSpan.FromSeconds(0)));
Console.WriteLine(dt.Ceil(TimeSpan.FromMinutes(1)));
Console.WriteLine(dt.Ceil(TimeSpan.FromMinutes(5)));
Console.WriteLine(dt.Ceil(TimeSpan.FromMinutes(10)));
Console.WriteLine(dt.Ceil(TimeSpan.FromMinutes(60)));
Console.WriteLine();
Console.WriteLine("Round:");
Console.WriteLine(dt.Round(TimeSpan.FromSeconds(0)));
Console.WriteLine(dt.Round(TimeSpan.FromMinutes(1)));
Console.WriteLine(dt.Round(TimeSpan.FromMinutes(5)));
Console.WriteLine(dt.Round(TimeSpan.FromMinutes(10)));
Console.WriteLine(dt.Round(TimeSpan.FromMinutes(60)));
Console.ReadKey();
</pre><br />
And here's the output:<br />
<br />
<pre class="consoleOut">Floor:
8/29/2010 3:26:23 PM
8/29/2010 3:26:00 PM
8/29/2010 3:25:00 PM
8/29/2010 3:20:00 PM
8/29/2010 3:00:00 PM
Ceil:
8/29/2010 3:26:23 PM
8/29/2010 3:27:00 PM
8/29/2010 3:30:00 PM
8/29/2010 3:30:00 PM
8/29/2010 4:00:00 PM
Round:
8/29/2010 3:26:23 PM
8/29/2010 3:26:00 PM
8/29/2010 3:25:00 PM
8/29/2010 3:30:00 PM
8/29/2010 3:00:00 PM
</pre>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-76602920329392521842010-08-24T14:26:00.001-05:002010-08-24T14:27:18.318-05:00Finding the calling method.I've been tasked with writing a logging component of a current application we're working on. My co-worker provided me with a snippet of code that has proven to be quite useful:<br />
<br />
<pre class="brush: c-sharp">using System.Diagnostics;
using System.Reflection;
.
.
.
//dig through the stack trace to find the calling method
StackTrace stackTrace = new StackTrace();
//since this is two methods removed from the caller, go two deep
StackFrame stackFrame = stackTrace.GetFrame(2);
MethodBase methodBase = stackFrame.GetMethod();
string caller = methodBase.ReflectedType + "." + methodBase.Name;
</pre><br />
I'm getting the third frame, because the method is two deep in the logger code, but you can use whatever number necessary to get the right frame. This gives you the whole Fully.Qualified.Namespace.Class.MethodName, which can be quite useful.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-46841036457611900222010-08-17T11:46:00.000-05:002010-08-17T11:46:42.398-05:00Dan Soltesz -blog | Silverlight datagrid double click behavior<a href="http://www.dansoltesz.com/post/2010/02/19/Silverlight-datagrid-double-click-behavior.aspx">Dan Soltesz -blog | Silverlight datagrid double click behavior</a><br /><br />Totally worth a look. Will follow up with more info later.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-14977598438981830092010-08-13T09:36:00.000-05:002010-08-13T09:36:31.144-05:00System.IO.DirectoryInfo extension method: Recursively print directory structureYeah, lots of extension method posts today. I'm just going through my projects and posting the good ones.<br />
<br />
Actually, this is one I did when I was looking at editing a .xlsx doc without Excel installed. It turns out that .xlsx docs are actually zip files. When decompressed, it's got several levels of directories. I wanted to show someone the directory structure on a forum, but I didn't feel like typing it all out, so I typed (more) code to do it for me. Amazing how we'll waste effort writing a program for something that was probably faster to do without it.<br />
<br />
I also used it as an excuse to learn about default parameters. C# now supports them. You could call this method by providing just one parameter, because all the others are provided automatically. Also note that you can use any TextWriter, not just Console.Out. You could make a <a href="http://msdn.microsoft.com/en-us/library/system.io.streamwriter.aspx">StreamWriter</a> and output it to text.<br />
<br />
Anyway, that was off topic. Here's the code.<br />
<br />
<pre class="brush: c-sharp">using System.IO;
namespace DirectoryExtensions
{
public static class DirectoryExtensions
{
public static void PrintDirectoryStructure(this DirectoryInfo directory, TextWriter writer, string prefix = ">", string spacer = "--")
{
writer.WriteLine(string.Format("{0}{1} (dir)", prefix, directory.Name));
prefix = spacer + prefix;
foreach (FileInfo file in directory.GetFiles())
writer.WriteLine(string.Format("{0}{1} (file)", prefix, file.Name));
DirectoryInfo[] subDirectories = directory.GetDirectories("*", SearchOption.TopDirectoryOnly);
if (subDirectories.Length > 0)
foreach (DirectoryInfo subDirectory in subDirectories)
PrintDirectoryStructure(subDirectory, writer, prefix, spacer);
}
}
}
</pre><br />
Here's a use case:<br />
<pre class="brush: c-sharp">DirectoryInfo directory = new DirectoryInfo(@"c:\dev\test");
directory.PrintDirectoryStructure(Console.Out);
</pre><br />
Expected output:<br />
<pre>>test (dir)
-->subdir1 (dir)
---->textfile.txt (file)
---->subdirA (dir)
---->subdirB (dir)
------>asdf.txt (file)
------>some text file.txt (file)
-->subdir2 (dir)
---->some text file.txt (file)
-->subdir3 (dir)</pre>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-10625841302872964242010-08-13T09:27:00.001-05:002010-08-13T09:27:42.802-05:00Extension method to export a Silverlight Datagrid to CSVWell, I couldn't figure out how to export it as an excel doc, but CSV is just as good...right? (Of course not, but it's good enough.)<br />
<br />
This is an extension method, so you'd have to call it with dataGrid1.Export();<br />
<br />
<pre class="brush: c-sharp">public static class DataGridExtensions
{
public static void Export(this DataGrid dg)
{
SaveExportedGrid(ExportDataGrid(true, dg));
}
public static void Export(this DataGrid dg, bool withHeaders)
{
SaveExportedGrid(ExportDataGrid(withHeaders, dg));
}
private static void SaveExportedGrid(string data)
{
SaveFileDialog sfd = new SaveFileDialog() { DefaultExt = "csv", Filter = "CSV Files (*.csv)|*.csv|All Files (*.*)|*.*", FilterIndex = 1 };
if (sfd.ShowDialog() ?? false)
{
using (StreamWriter sr = new StreamWriter(sfd.OpenFile()))
sr.Write(data);
}
}
private static string ExportDataGrid(bool withHeaders, DataGrid grid)
{
string colPath;
System.Reflection.PropertyInfo propInfo;
System.Windows.Data.Binding binding;
System.Text.StringBuilder strBuilder = new System.Text.StringBuilder();
System.Collections.IList source = (grid.ItemsSource as System.Collections.IList);
if (source == null)
return "";
List<string> headers = new List<string>();
grid.Columns.ToList().ForEach(col =>
{
if (col is DataGridBoundColumn)
{
headers.Add(FormatCSVField(col.Header.ToString()));
}
});
strBuilder
.Append(String.Join(",", headers.ToArray()))
.Append("\r\n");
foreach (Object data in source)
{
List<string> csvRow = new List<string>();
foreach (DataGridColumn col in grid.Columns)
{
if (col is DataGridBoundColumn)
{
binding = (col as DataGridBoundColumn).Binding;
colPath = binding.Path.Path;
propInfo = data.GetType().GetProperty(colPath);
if (propInfo != null)
{
csvRow.Add(FormatCSVField(propInfo.GetValue(data, null).ToString()));
}
}
}
strBuilder
.Append(String.Join(",", csvRow.ToArray()))
.Append("\r\n");
}
return strBuilder.ToString();
}
private static string FormatCSVField(string data)
{
return String.Format("\"{0}\"",
data.Replace("\"", "\"\"\"")
.Replace("\n", "")
.Replace("\r", "")
);
}
}</pre>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-22913150300890806452010-08-13T09:22:00.000-05:002010-08-13T09:22:57.996-05:00Convert Word and Excel to PDFs in C#I had a very repetitive task I had to perform. Take a directory full of Word documents, open each, and do a Save As -> PDF on each of them. Not in this lifetime. So I wrote a quick program for it, and included Excel just to boot. I'll show the important methods.<br />
<br />
You must have Office 2007 or greater installed on the machine the code will run on. <br />
<br />
Add a reference to: <br />
Microsoft Excel 12.0 Object Library (or greater) <br />
Microsoft Word 12.0 Object Library (or greater) <br />
<br />
Both can be found in the COM tab of the Add Reference dialog.<br />
<br />
Here's the code:<br />
<pre class="brush: c-sharp">
using System;
using Word = Microsoft.Office.Interop.Word;
using Excel = Microsoft.Office.Interop.Excel;
...
void ExportExcel(string infile, string outfile)
{
Excel.Application excelApp = null;
try
{
excelApp = new Excel.Application();
excelApp.Workbooks.Open(infile);
excelApp.ActiveWorkbook.ExportAsFixedFormat(Excel.XlFixedFormatType.xlTypePDF, outfile);
}
finally
{
if (excelApp != null)
excelApp.Quit();
}
}
void ExportWord(string infile, string outfile)
{
Word.Application wordApp = null;
try
{
wordApp = new Word.Application();
wordApp.Documents.Open(infile);
wordApp.ActiveDocument.ExportAsFixedFormat(outfile, Word.WdExportFormat.wdExportFormatPDF);
}
finally
{
if (wordApp != null)
wordApp.Quit();
}
}
</pre>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com1tag:blogger.com,1999:blog-5956282563643600179.post-88267447314648820932010-08-12T17:30:00.004-05:002010-08-12T17:37:59.060-05:00Some new extension methods for System.StringSo AdamSpeight2008 posted a <a href="http://www.dreamincode.net/forums/topic/185598-unique-ways-to-accomplish-a-task-task-3/">small challenge</a> for the VB.NET section, and it got me thinking. That would be a pretty useful extension method. I'd previously written a RemoveMany extension for strings, might as well add a ReplaceMany. Also, I'll throw in a ToTitleCase for free.<br />
<br />
So, here they are:<br />
<pre class="brush: c-sharp">public static class Extensions
{
public static string ReplaceMany(this string dirty, Dictionary<char, char> replacer)
{
StringBuilder sb = new StringBuilder();
dirty.ToList().ForEach(x => sb.Append(replacer.ContainsKey(x)
? replacer[x]
: x));
return sb.ToString();
}
public static string ReplaceMany(this string dirty, params Tuple<char, char>[] pairs)
{
StringBuilder sb = new StringBuilder();
dirty.ToList().ForEach(x => sb.Append(pairs.Any(y => y.Item1 == x)
? pairs.First(y => y.Item1 == x).Item2
: x));
return sb.ToString();
}
public static string ReplaceMany(this string dirty, params Tuple<string, string>[] pairs)
{
pairs.ToList().ForEach(x => dirty = dirty.Replace(x.Item1, x.Item2));
return dirty;
}
public static string RemoveMany(this string dirtyString, params string[] stringsToRemove)
{
return dirtyString.Split(stringsToRemove, StringSplitOptions.None)
.Aggregate((sentence, next) => sentence + next);
}
public static string ToTitleCase(this string sentence)
{
return System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo.ToTitleCase(sentence);
}
}
</pre><br />
<s>Note that the ReplaceMany uses a Dictionary, which I'm not exactly thrilled about, but off the top of my head I can't think of any easy and elegant way to do things differently. Maybe tuples.</s><br />
<br />
Definitely tuples. I've added an overload that takes tuples of chars, and tuples of strings.<br />
<br />
Here's some code to test them and show usage, if you're interested.<br />
<pre class="brush: c-sharp">Dictionary<char, char> dict =
new Dictionary<char, char>() { { 'D', 'a' }, { 'a', 'D' }, { ' ', 'X' } };
Console.WriteLine("Dream In Code".ReplaceMany(dict));
Console.WriteLine("Bytes.com".ReplaceMany(Tuple.Create('B', 'c'),
Tuple.Create('c', 'B'), Tuple.Create('.', 'U')));
Console.WriteLine("Bob is not your old uncle".ReplaceMany(Tuple.Create("not", "surely"),
Tuple.Create("old", "young")));
Console.WriteLine("Texstying".RemoveMany("x", "y"));
Console.WriteLine("this sHould be in title caSE".ToTitleCase());
Console.ReadKey();
</pre>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-14721135798319524702010-08-12T15:27:00.000-05:002010-08-12T15:27:54.514-05:00Ridiculously overambitious projectsI see this all the time on forums. Here's an example:<br />
<a href="http://www.dreamincode.net/forums/topic/185703-face-recognition-with-c%23/">http://www.dreamincode.net/forums/topic/185703-face-recognition-with-c%23/</a><br />
<br />
As you can see, my answer was less than complimentary. But I see this stuff all the time. People asking "how to create antivirus software" or "how to make 3d game."<br />
<br />
I have no idea where they get these ideas. Is some insane professor assigning them super hard homework? Do they think that just because they wrote their first Hello World program and it compiled with no errors that they're ready for the big leagues?<br />
<br />
More likely, they have no idea how complicated the topics they want to try are. But that doesn't stop me from saying WTF! every time I see these <s>stupid questions</s> questions from stupid people.<br />
<s></s>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com1tag:blogger.com,1999:blog-5956282563643600179.post-39173798362379895252010-08-12T11:04:00.001-05:002010-08-12T11:05:28.300-05:00Silverlight ChildWindows and Focus<a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.childwindow(VS.95).aspx">ChildWindow</a> is an awesome control. No doubt about that. It saves me a lot of time and effort creating my own. But it's not without it's quirks.<br />
<br />
I can't figure out exactly when, but a ChildWindow will somehow <a href="http://msdn.microsoft.com/en-us/library/system.windows.controls.control.focus(v=VS.95).aspx">.Focus()</a> itself, after the constructor and <i>after the ChildWindow_Loaded</i> <i>event. </i>This was driving me crazy. I'd focus my TextBox in the Loaded event, see the cursor blink once, and then vanish.<br />
<br />
So here's a clunky hack to get around it:<br />
<pre class="brush: c-sharp">private bool firstFocus = true;
//...
private void ChildWindow_GotFocus(object sender, RoutedEventArgs e)
{
if (firstFocus)
{
textBox1.Focus();
firstFocus = false;
}
}
</pre><br />
<span class="Apple-style-span" style="font-size: small;"><a href="http://stackoverflow.com/questions/1892891/how-to-set-focus-on-textbox-in-silverlight-4-out-of-browser-popup">Credit where credit is due.</a></span>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-35331244395711551932010-08-12T10:12:00.000-05:002010-08-12T10:12:57.189-05:00Hmm...feature creep? In MY project?Ugh. I've dealt with this so many times it shouldn't be a surprise. Right near the end of a project, someone comes up with a brilliant idea that should be implemented. It's a good idea, but it'll take extra work, and we're already near turning the damn thing in. Of course, this person that thinks it up is in a position that he can make it reality.<br />
<br />
Also, changes to the damn spec. We have a set XML schema that we've used and revised over and over again until we thought it was perfect. Then we built an application around it. Then someone "discovers" that we need to change an important part of it. This project is never going to end.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-67105009968681583102010-08-10T12:06:00.004-05:002010-08-10T15:06:18.212-05:00On the importance of logging...Today I fought with an issue for hours. I have a Silverlight frontend that calls a WCF service to edit a file. Everything worked in debug mode, but once deployed, it only worked for about half the people that tried it, with no obvious correlation between them.<br />
<br />
I kept thinking that the issue was with the service. After all, I'd seen errors similar to this. Maybe it's some permissions thing. Maybe the binding or behaviors aren't set properly. Maybe the client config isn't set up right.<br />
<br />
And maybe I was chasing my tail. I was about ready to quit when a colleague suggested actually <i>using</i> the logging service we set up. So I put debug logging messages in at each point in the flow. Guess what? The service was never even being called. The issue was happening <i>before </i>the service call.<br />
<br />
It was a stupid, simple issue that should have never been there in the first place.<br />
<pre class="brush: csharp">contactList.Single(x => x.Address == userEmailAddress).Timestamp = DateTime.Now;</pre>Should have been<br />
<pre class="brush: csharp">contactList.Single(x => x.Address.ToLower() == userEmailAddress.ToLower())
.Timestamp = DateTime.Now;</pre><br />
<span class="Apple-style-span" style="font-size: small;"><i>side note: How silly is it that some people in the same company have email addresses with a different case? We tested this on six people. Three of the people had lower case domain names in their email (like @gmail.com). The other three had capital letters (@GMAIL.com). Ugh.</i></span><br />
<br />
Simple little things like this can ruin your day. My colleague called this an "object lesson." Use logging. Set it up so that you can put debug messages everywhere, and just flip a bit in the config file to stop logging them, but continue to log errors.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-69720880157715210582010-08-10T11:58:00.000-05:002010-08-10T11:58:09.736-05:00How forum experts know you're not worth our time.Most of the people who answer questions on forums are professionals. We do this whole "writing code thing" for a living. Most of us have put up with enough shit just from work that we won't hesitate for a second to ignore your question if you give us an excuse. That's not to say we're jerks, looking for reasons to ignore you; it means that we're not going to work really hard to help you when you make it hard for us. <br />
<br />
There are many obvious indicators, and I'll go through a few of them now. Hopefully you don't make any of these mistakes.<br />
<ol><li><b>No source code.</b> I'm not sure how many hundreds of times I've had to type this in one way or another, but <b>we aren't going to write your project for you.</b> Homework or actual work; it doesn't matter. None of us are going to do your job for you. Most of us love helping. The greatest reward for a forum expert is to see that someone comprehended the answer you posted and applied it to solve their problems.<br />
<br />
Handing you the code is not teaching. It's cheating. And it's also working for free. Screw that.<br />
<br />
</li>
<li><b>Spelling.</b> Look, I understand English may not be your first language. That's fine, I don't expect you to spell everything perfectly. But for those of you that do speak English, I expect you to type it properly as well. Every modern browser has a built-in spell checker, and IE has addons that provide that capability. Get one. It honestly can make the difference. It lets us know that you respect us enough to treat us like the professionals we are. <br />
<br />
On that note, if you type "plz," especially with numerous extra "z"s, <b>I close the browser tab and ignore your question.</b> It's a personal peeve, but one I've seen reflected in many of the other experts on the forums I frequent. Just don't do it. <br />
<br />
And it's "code," not "codes" or "codez."<br />
<br />
</li>
<li><b>Overstating urgency.</b> I'm a volunteer. You're not paying me. Because of that, my time is mine, not yours. Just because you "urgently need codes plzzzz send quick" doesn't mean that I'm going to go out of my way to help. Your poor planing does not create an emergency on my part. I'll do my best to help, but don't start getting frustrated or impatient or I'll walk away and never look back.<br />
<br />
If your problem is urgent, clearly state so. A real timeline helps: "This is actually pretty urgent. The project is due in 48 hours." If I can help you in that timeframe, I'll do my best to. But that's different than just freaking out and begging. Which leads me to...<br />
<br />
</li>
<li><b>Begging.</b> The entire point of this (and any) programming forum is for experts to help you. You don't need to beg us. If we can help, we will. Saying "plz plzzzz help im clueless help plz" isn't incentive for us to help. It's actually the opposite.<br />
<br />
The best thing you can do to get help is clearly state your problems, what you've tried to resolve them, the code where it's happening, and any error/exception messages. Which is a segue into:<br />
<br />
</li>
<li><b>Lack of effort/clarity.</b> This is the big one. This is probably the most common way that we decide you're not worth our time. If it's not worth your time to clearly post what your problem is, then it's not worth our time to help you fix it.</li>
</ol>I'll expand on effort and clarity, because it deserves its own section. Here are some dos and don'ts.<br />
<br />
<b>Dont...</b><br />
<br />
<ul><li><span class="Apple-style-span" style="color: #222222; font-family: georgia; font-size: 12px; line-height: 19px;">...only post one sentence. If your question is simple enough to be fit into a single sentence, it's probably simple enough to use google to answer.</span></li>
<li><span class="Apple-style-span" style="color: #222222; font-family: georgia; font-size: 12px; line-height: 19px;">...post no code. We need to see your code to solve your problem. Conversely, </span></li>
<li><span class="Apple-style-span" style="color: #222222; font-family: georgia; font-size: 12px; line-height: 19px;">...post ALL your code. We don't need to see 4000 lines of auto-generated garbage to solve a problem in one method.</span></li>
<li><span class="Apple-style-span" style="color: #222222; font-family: georgia; font-size: 12px; line-height: 19px;">...just say "It doesn't work." That's the most useless statement ever. What doesn't work? Does anything happen? Nothing? Is your code being executed? Does it throw an exception? These are the important things we need to know.</span></li>
</ul><div><span class="Apple-style-span" style="color: #222222; font-family: georgia; font-size: small;"><span class="Apple-style-span" style="font-size: 12px; line-height: 19px;"><b>Do...</b></span></span></div><div><span class="Apple-style-span" style="color: #222222; font-family: georgia; font-size: small;"><span class="Apple-style-span" style="font-size: 12px; line-height: 19px;"></span></span><br />
<span class="Apple-style-span" style="color: #222222; font-family: georgia; font-size: small;"><span class="Apple-style-span" style="font-size: 12px; line-height: 19px;"><div><ul><li>...clearly post your entire problem.</li>
<li>...post relevant source code.</li>
<li>...post any exceptions, and indicate which line they're thrown at.</li>
<li>...post the steps you've tried to resolve your own problem.</li>
<li>...respect the professionals trying to help you. This one's important!</li>
</ul><div>Following these simple rules makes it much more likely that someone is going to help you. Number one rule: respect us, and we'll respect you.</div></div></span></span></div>Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com3tag:blogger.com,1999:blog-5956282563643600179.post-75611725296898889052010-08-10T11:34:00.007-05:002010-08-11T09:19:17.663-05:00Syntax HighlightingLets test the syntax highlighting:<br />
<br />
<pre class="brush: c-sharp">using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.ReadKey();
}
}
}
</pre><br />
Now one with line level highlighting:<br />
<br />
<pre class="brush: c-sharp; highlight: [9, 10]">using System;
namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
Console.ReadKey();
}
}
}
</pre><br />
Nice.<br />
<br />
<s>Happened to find the instructions for syntax highlighting on </s><a href="http://lukabloga.blogspot.com/2008/10/to-test-new-highlighting.html"><s>Luka Marinko's blog</s></a><s>. </s> These are good instructions for using <a href="http://code.google.com/p/google-code-prettify/">Prettify</a>, but I like SyntaxHighligher better.<br />
<br />
Anyway, here's how I enabled this. Paste this code, and <a href="http://alexgorbatchev.com/SyntaxHighlighter/manual/brushes/">any other brushes</a> that you'd like directly above the </head> tag in your template.<br />
<pre class="brush: js; html-script: true"><link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css"></link>
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeDefault.css" rel="stylesheet" type="text/css"></link>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushVb.js" type="text/javascript"></script>
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js" type="text/javascript"></script>
<script language='javascript'>
//<![CDATA[
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.defaults['toolbar'] = false;
SyntaxHighlighter.config.clipboardSwf = 'http://alexgorbatchev.com/pub/sh/current/scripts/clipboard.swf';
SyntaxHighlighter.all();
//]]>
</script>
</pre><br />
Lots and lots of props to Alex Gorbatchev for creating <a href="http://alexgorbatchev.com/SyntaxHighlighter/">SyntaxHighlighter</a>, and hosting the .js files for those of us that can't host them ourselves.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com0tag:blogger.com,1999:blog-5956282563643600179.post-36310136511506977722010-08-10T11:22:00.000-05:002010-08-10T11:22:10.448-05:00Hello World!Well, this is my first blog post on my new blog. Hopefully I can stick with it and keep it updated frequently.Curtis Rutlandhttp://www.blogger.com/profile/08704264771476604784noreply@blogger.com1