Creating a form with additional dynamic fields specified by customer, options?
We have a desktop app which basically models a job for a specific sector (Transport for example) with things like job date, references, driver allocation etc which is then sent to/from PDAs for status updates.
Although it's mainly off the shelf, we usually end up having to do bespoke parts to suit the company, which about 90% of the time it only additional data fields that don't really require any logic, only storing / retrieval.
I've been looking around for a basic drag and drop library to turn a form into an editable mode that I could save position, component type, default value and populate at runtime in non-edit mode but haven't really been able to find one.
Is the best way to just roll my own or am I missing a library that will get me 60% of the way there? WPF or Winf开发者_如何学Corms help would be appreciated (we're using Winforms but moving to WPF).
Cheers,
Thomas
I'd suggest you just write your own. In the most basic case of a form with a vertical list of labelled controls my intuitive approach would be to have a data object that consists of a ordered list of string-object-pairs (maybe make it a triple with a validation rule too), when the form should be loaded each object's type is checked and if it is a string you create a textbox, if it's a bool you create a checkbox etc.
You can also have input-validation if you have int and doubles for example. The other direction shouldn't be too hard either.
I've written semi-dynamic generic edit dialogues before like the following (WPF):
Window Content XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition />
</Grid.RowDefinitions>
<StackPanel Name="StackPanelInput" Grid.Row="0" Orientation="Vertical" VerticalAlignment="Top" Margin="5"/>
<StackPanel Grid.Row="1" Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right" Margin="5">
<Button Name="ButtonOK" HorizontalAlignment="Right" Width="100" Margin="5" Click="ButtonOK_Click" IsDefault="True">OK</Button>
<Button Name="ButtonCancel" HorizontalAlignment="Right" Width="100" Margin="5" Click="ButtonCancel_Click" IsCancel="True">Cancel</Button>
</StackPanel>
</Grid>
Code-behind:
public partial class EditDialog : Window
{
private List<Control> Controls = new List<Control>();
public EditDialog()
{
InitializeComponent();
Loaded += delegate { KeyboardFocusFirstControl(); };
}
public EditDialog(string dialogTitle) : this()
{
Title = dialogTitle;
}
private void ButtonOK_Click(object sender, RoutedEventArgs e)
{
(sender as Button).Focus();
if (!IsValid(this))
{
MessageBox.Show("Some inputs are currently invalid.");
return;
}
DialogResult = true;
}
private void ButtonCancel_Click(object sender, RoutedEventArgs e)
{
DialogResult = false;
}
bool IsValid(DependencyObject node)
{
if (node != null)
{
bool isValid = !Validation.GetHasError(node);
if (!isValid)
{
if (node is IInputElement) Keyboard.Focus((IInputElement)node);
return false;
}
}
foreach (object subnode in LogicalTreeHelper.GetChildren(node))
{
if (subnode is DependencyObject)
{
if (IsValid((DependencyObject)subnode) == false) return false;
}
}
return true;
}
public TextBox AddTextBox(string label, ValidationRule validationRule)
{
Grid grid = new Grid();
grid.Height = 25;
grid.Margin = new Thickness(5);
ColumnDefinition colDef1 = new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) };
ColumnDefinition colDef2 = new ColumnDefinition() { Width = new GridLength(1, GridUnitType.Star) };
grid.ColumnDefinitions.Add(colDef1);
grid.ColumnDefinitions.Add(colDef2);
Label tbLabel = new Label() { Content = label };
tbLabel.HorizontalAlignment = System.Windows.HorizontalAlignment.Left;
grid.Children.Add(tbLabel);
TextBox textBox = new TextBox();
Binding textBinding = new Binding("Text");
textBinding.Source = textBox;
textBinding.ValidationRules.Add(validationRule);
textBox.SetBinding(TextBox.TextProperty, textBinding);
textBox.GotKeyboardFocus += delegate { textBox.SelectAll(); };
textBox.TextChanged += delegate { textBox.GetBindingExpression(TextBox.TextProperty).ValidateWithoutUpdate(); };
textBox.Text = "";
textBox.GetBindingExpression(TextBox.TextProperty).ValidateWithoutUpdate();
Grid.SetColumn(textBox, 1);
grid.Children.Add(textBox);
StackPanelInput.Children.Add(grid);
Controls.Add(textBox);
return textBox;
}
public TextBox AddTextBox(string label, ValidationRule validationRule, string defaultText)
{
TextBox tb = AddTextBox(label, validationRule);
tb.Text = defaultText;
return tb;
}
public CheckBox AddCheckBox(string label)
{
Grid grid = new Grid();
grid.Height = 25;
grid.Margin = new Thickness(5);
CheckBox cb = new CheckBox();
cb.VerticalAlignment = System.Windows.VerticalAlignment.Center;
cb.Content = label;
grid.Children.Add(cb);
StackPanelInput.Children.Add(grid);
Controls.Add(cb);
return cb;
}
private void KeyboardFocusFirstControl()
{
if (Controls.Count > 0)
{
Keyboard.Focus(Controls[0]);
}
}
}
Usage example:
EditDialog diag = new EditDialog();
TextBox firstName = diag.AddTextBox("First Name:", new StringValidationRule());
TextBox lastName = diag.AddTextBox("Last Name:", new StringValidationRule());
TextBox age = diag.AddTextBox("Age:", new IntegerValidationRule(1,int.MaxValue));
if ((bool)diag.ShowDialog())
{
//parse texts and write them to some data;
}
With some custom constructors, edit buttons and the like i am sure you'll be able to turn it into a fully dynamic dialogue. Depending on how sophisticated the layout should be that might be more or less work. Of course finding a library that does just that would be easiest, maybe someone else knows one.
精彩评论