void Command_Files_Create_FromExpressions(CreateFilesDialog.Result result)
		{
			var variables = GetVariables();

			var filenameExpression = new NEExpression(result.FileName);
			var dataExpression = new NEExpression(result.Data);
			var resultCount = variables.ResultCount(filenameExpression, dataExpression);

			var filename = filenameExpression.EvaluateRows<string>(variables, resultCount);
			var data = dataExpression.EvaluateRows<string>(variables, resultCount);
			for (var ctr = 0; ctr < data.Count; ++ctr)
				File.WriteAllBytes(filename[ctr], Coder.StringToBytes(data[ctr], result.CodePage, true));
		}
		void Command_Files_Operations_CopyMove(CopyMoveFilesDialog.Result result, bool move)
		{
			var variables = GetVariables();

			var oldFileNameExpression = new NEExpression(result.OldFileName);
			var newFileNameExpression = new NEExpression(result.NewFileName);
			var resultCount = variables.ResultCount(oldFileNameExpression, newFileNameExpression);

			var oldFileNames = oldFileNameExpression.EvaluateRows<string>(variables, resultCount);
			var newFileNames = newFileNameExpression.EvaluateRows<string>(variables, resultCount);

			const int InvalidCount = 10;
			var invalid = oldFileNames.Distinct().Where(name => !FileOrDirectoryExists(name)).Take(InvalidCount).ToList();
			if (invalid.Any())
				throw new Exception($"Sources don't exist:\n{string.Join("\n", invalid)}");

			invalid = newFileNames.Select(name => Path.GetDirectoryName(name)).Distinct().Where(dir => !Directory.Exists(dir)).Take(InvalidCount).ToList();
			if (invalid.Any())
				throw new Exception($"Directories don't exist:\n{string.Join("\n", invalid)}");

			// If user specified a file and a directory, assume they want the file (with the same name) in that directory
			newFileNames = newFileNames.Zip(oldFileNames, (newFileName, oldFileName) => (File.Exists(oldFileName)) && (Directory.Exists(newFileName)) ? Path.Combine(newFileName, Path.GetFileName(oldFileName)) : newFileName).ToList();

			invalid = oldFileNames.Zip(newFileNames, (oldFileName, newFileName) => new { oldFileName, newFileName }).Where(obj => (Directory.Exists(obj.newFileName)) || ((Directory.Exists(obj.oldFileName)) && (File.Exists(obj.newFileName)))).Select(pair => pair.newFileName).Distinct().Take(InvalidCount).ToList();
			if (invalid.Any())
				throw new Exception($"Destinations already exist:\n{string.Join("\n", invalid)}");

			if (new Message(WindowParent)
			{
				Title = "Confirm",
				Text = $"Are you sure you want to {(move ? "move" : "copy")} these {resultCount} files/directories?",
				Options = Message.OptionsEnum.YesNo,
				DefaultAccept = Message.OptionsEnum.Yes,
				DefaultCancel = Message.OptionsEnum.No,
			}.Show() != Message.OptionsEnum.Yes)
				return;

			invalid = newFileNames.Where(pair => File.Exists(pair)).Distinct().Take(InvalidCount).ToList();
			if (invalid.Any())
			{
				if (new Message(WindowParent)
				{
					Title = "Confirm",
					Text = $"Are you sure you want to overwrite these files:\n{string.Join("\n", invalid)}",
					Options = Message.OptionsEnum.YesNo,
					DefaultAccept = Message.OptionsEnum.Yes,
					DefaultCancel = Message.OptionsEnum.No,
				}.Show() != Message.OptionsEnum.Yes)
					return;
			}

			for (var ctr = 0; ctr < oldFileNames.Count; ++ctr)
				if (Directory.Exists(oldFileNames[ctr]))
				{
					if (move)
						Directory.Move(oldFileNames[ctr], newFileNames[ctr]);
					else
						CopyDirectory(oldFileNames[ctr], newFileNames[ctr]);
				}
				else
				{
					if (File.Exists(newFileNames[ctr]))
						File.Delete(newFileNames[ctr]);

					if (move)
						File.Move(oldFileNames[ctr], newFileNames[ctr]);
					else
						File.Copy(oldFileNames[ctr], newFileNames[ctr]);
				}
		}
		void UpdateChildren()
		{
			List<string> variables;
			List<string> results;
			Dictionary<string, List<string>> varValues;
			var useResults = MultiRow ? Math.Max(0, (int)((ActualHeight - Spacing * 2) / RowHeight - 1)) : MaxResults;
			try
			{
				var expression = new NEExpression(Expression);
				variables = new List<string>(expression.Variables);
				var resultCount = Math.Min(NumResults ?? ResultCount, useResults);
				results = expression.EvaluateRows<string>(Variables, resultCount).Coalesce("").ToList();
				varValues = variables.ToDictionary(variable => variable, variable => Variables.GetValues(variable, resultCount).Select(val => val?.ToString()).ToList());
				IsValid = true;
				ErrorMessage = null;
			}
			catch (Exception ex)
			{
				results = MultiRow ? Enumerable.Repeat(default(string), useResults).ToList() : new List<string>();
				variables = new List<string>();
				varValues = new Dictionary<string, List<string>>();
				IsValid = false;
				ErrorMessage = ex.Message;
				if ((Children.Count != 0) || (ActualHeight == 0) || (ActualWidth == 0))
					return;
			}

			Children.Clear();
			ColumnDefinitions.Clear();
			RowDefinitions.Clear();

			Func<WidthType, int, Tuple<WidthType, List<FrameworkElement>>> GetLine = (widthType, numRows) => Tuple.Create(widthType, Enumerable.Range(0, numRows).Select(row => new Rectangle { Width = Spacing, Fill = BackColor }).Cast<FrameworkElement>().ToList());

			var columns = new List<Tuple<WidthType, List<FrameworkElement>>>();

			if (MultiRow)
			{
				Func<WidthType, int, Tuple<WidthType, List<FrameworkElement>>> GetSpace = (widthType, numRows) => Tuple.Create(widthType, new[] { new Rectangle { Width = Spacing, Fill = BackColor } }.Concat(Enumerable.Range(0, numRows - 1).Select(row => new Rectangle { Width = Spacing })).Cast<FrameworkElement>().ToList());
				columns.Add(GetLine(WidthType.None, results.Count + 1));
				columns.AddRange(variables.SelectMany(variable => new[] {
					GetSpace(WidthType.Shrink3, results.Count + 1),
					Tuple.Create(WidthType.Expand | WidthType.Shrink4, new[] { GetTextBlock(variable, BackColor) }.Concat(Enumerable.Range(0, results.Count).Select(result => GetTextBlock(varValues[variable][result]))).ToList()),
					GetSpace(WidthType.Shrink3, results.Count + 1),
					GetLine(WidthType.Shrink5, results.Count + 1),
				}));
				if (!variables.Any())
				{
					columns.Add(GetSpace(WidthType.Shrink3, results.Count + 1));
					columns.Add(Tuple.Create(WidthType.Expand | WidthType.Shrink4, new[] { GetTextBlock("", BackColor) }.Concat(results.Select(result => GetTextBlock("<No vars>"))).ToList()));
					columns.Add(GetSpace(WidthType.Shrink3, results.Count + 1));
					columns.Add(GetLine(WidthType.Shrink5, results.Count + 1));
				}

				columns.Add(Tuple.Create(WidthType.Shrink1, new[] { default(TextBlock) }.Concat(results.Select(result => GetTextBlock(" => "))).ToList()));
				columns.Add(GetLine(WidthType.Shrink2, results.Count + 1));

				columns.Add(Tuple.Create(WidthType.Shrink6, new[] { new Rectangle { Width = Spacing, Fill = BackColor } }.Concat(Enumerable.Repeat(default(FrameworkElement), results.Count)).ToList()));
				columns.Add(Tuple.Create(WidthType.Expand | WidthType.Shrink7, new[] { GetTextBlock("Result", BackColor) }.Concat(results.Select(result => GetTextBlock(result))).ToList()));
				columns.Add(Tuple.Create(WidthType.Shrink6, new[] { new Rectangle { Width = Spacing, Fill = BackColor } }.Concat(Enumerable.Repeat(default(FrameworkElement), results.Count)).ToList()));

				columns.Add(GetLine(WidthType.None, results.Count + 1));
			}
			else
			{
				Func<WidthType, Tuple<WidthType, List<FrameworkElement>>> GetSpace = widthType => Tuple.Create(widthType, new[] { new Rectangle { Width = Spacing } }.Cast<FrameworkElement>().ToList());
				columns.Add(GetLine(WidthType.None, 1));
				if (results.Count == 0)
				{
					columns.Add(Tuple.Create(WidthType.Expand | WidthType.Shrink1, new List<FrameworkElement> { default(FrameworkElement) }));
					columns.Add(GetLine(WidthType.None, 1));
				}
				else
					columns.AddRange(results.SelectMany(result => new[] {
						GetSpace(WidthType.Shrink1),
						Tuple.Create(WidthType.Expand | WidthType.Shrink2, new List<FrameworkElement> { GetTextBlock(result) }),
						GetSpace(WidthType.Shrink1),
						GetLine(WidthType.Shrink3, 1),
					}).ToList());
			}

			// Sanity check: should have same number on all columns
			if (columns.Select(column => column.Item2.Count).Distinct().Count() != 1)
				throw new Exception("Column counts don't match");

			// Measure everything
			var margin = new Thickness(0);
			var size = new Size(double.PositiveInfinity, double.PositiveInfinity);
			columns.SelectMany(column => column.Item2).Where(element => element != null).ForEach(element => { element.Margin = margin; element.Measure(size); });
			const int precision = 10000;
			var widths = columns.Select(column => (int)(column.Item2.Max(text => text?.DesiredSize.Width * precision) ?? 0)).ToList();
			var available = Math.Max(0, (int)ActualWidth * precision);

			// Expand as necessary
			var expandIndexes = columns.Indexes(column => column.Item1.HasFlag(WidthType.Expand)).ToList();
			var extraPerColumn = (available - widths.Sum()) / expandIndexes.Count;
			if (extraPerColumn > 0)
				expandIndexes.ForEach(index => widths[index] += extraPerColumn);

			// Shrink as necessary
			var shrinks = new[] { WidthType.Shrink1, WidthType.Shrink2, WidthType.Shrink3, WidthType.Shrink4, WidthType.Shrink5, WidthType.Shrink6, WidthType.Shrink7 };
			foreach (var shrink in shrinks)
			{
				var shrinkIndexes = columns.Indexes(column => column.Item1.HasFlag(shrink)).ToList();
				while ((widths.Sum() > available) && (shrinkIndexes.Any(index => widths[index] != 0)))
				{
					var shrinkWidths = shrinkIndexes.Select(column => widths[column]).OrderByDescending().Distinct().ToList();
					var maxIndexes = shrinkIndexes.Where(column => widths[column] == shrinkWidths[0]).ToList();
					var minWidth = shrinkWidths.Count > 1 ? shrinkWidths[1] : 0;
					var extraPerIndex = (widths.Sum() - available + maxIndexes.Count - 1) / maxIndexes.Count;
					maxIndexes.ForEach(column => widths[column] = Math.Max(minWidth, widths[column] - extraPerIndex));
				}
			}

			var rows = columns[0].Item2.Count;
			RowDefinitions.Add(new RowDefinition { Height = new GridLength(Spacing) });
			for (var row = 0; row < rows; ++row)
				RowDefinitions.Add(new RowDefinition { Height = new GridLength(RowHeight) });
			RowDefinitions.Add(new RowDefinition { Height = new GridLength(Spacing) });

			for (var column = 0; column < columns.Count; ++column)
			{
				ColumnDefinitions.Add(new ColumnDefinition { Width = new GridLength((double)widths[column] / precision) });
				for (var row = 0; row < rows; ++row)
					AddChild(columns[column].Item2[row], row + 1, column);
			}

			AddChild(new Rectangle { Fill = BackColor, Height = Spacing }, 0, 0, columnSpan: ColumnDefinitions.Count);
			AddChild(new Rectangle { Fill = BackColor, Height = Spacing }, RowDefinitions.Count - 1, 0, columnSpan: ColumnDefinitions.Count);

			GetErrorControls().ForEach(control => AddChild(control, 0, 0, RowDefinitions.Count, ColumnDefinitions.Count));
		}