Update Reference Sources to .NET Framework 4.6.1
[mono.git] / mcs / class / referencesource / System.Activities.Core.Presentation / System / Activities / Core / Presentation / CaseKeyBox.ViewModel.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.Activities.Core.Presentation
6 {
7     using System;
8     using System.Collections.ObjectModel;
9     using System.Windows;
10     using System.Linq;
11     using System.ComponentModel;
12     using System.Runtime;
13     using System.Diagnostics.CodeAnalysis;
14     using System.Globalization;
15     using System.Activities.Presentation.Model;
16
17     class CaseKeyBoxViewModel : DependencyObject
18     {
19         static readonly string Null = "(null)";
20         static readonly string Empty = "(empty)";
21
22         public static readonly DependencyProperty ComboBoxIsEditableProperty =
23             DependencyProperty.Register("ComboBoxIsEditable", typeof(bool), typeof(CaseKeyBoxViewModel), new UIPropertyMetadata(false));
24
25         public static readonly DependencyProperty ComboBoxVisibilityProperty =
26             DependencyProperty.Register("ComboBoxVisibility", typeof(Visibility), typeof(CaseKeyBoxViewModel), new UIPropertyMetadata(Visibility.Collapsed));
27
28         public static readonly DependencyProperty ComboBoxItemsProperty =
29             DependencyProperty.Register("ComboBoxItems", typeof(ObservableCollection<string>), typeof(CaseKeyBoxViewModel));
30
31         public static readonly DependencyProperty DataTemplateNameProperty =
32             DependencyProperty.Register("DataTemplateName", typeof(string), typeof(CaseKeyBoxViewModel), new UIPropertyMetadata("Label"));
33
34         public static readonly DependencyProperty TextProperty =
35             DependencyProperty.Register("Text", typeof(string), typeof(CaseKeyBoxViewModel), new UIPropertyMetadata(String.Empty));
36
37         public static readonly DependencyProperty TextBoxVisibilityProperty =
38             DependencyProperty.Register("TextBoxVisibility", typeof(Visibility), typeof(CaseKeyBoxViewModel), new UIPropertyMetadata(Visibility.Visible));
39
40         public const string BoxesTemplate = "Boxes";
41         public const string LabelTemplate = "Label";
42
43         string oldText = String.Empty;
44
45         public CaseKeyBoxViewModel(ICaseKeyBoxView view)
46         {
47             this.View = view;
48         }
49
50         public bool ComboBoxIsEditable
51         {
52             get { return (bool)GetValue(ComboBoxIsEditableProperty); }
53             set { SetValue(ComboBoxIsEditableProperty, value); }
54         }
55
56         public ObservableCollection<string> ComboBoxItems
57         {
58             get { return (ObservableCollection<string>)GetValue(ComboBoxItemsProperty); }
59             set { SetValue(ComboBoxItemsProperty, value); }
60         }
61
62         public Visibility ComboBoxVisibility
63         {
64             get { return (Visibility)GetValue(ComboBoxVisibilityProperty); }
65             set { SetValue(ComboBoxVisibilityProperty, value); }
66         }
67
68         public string DataTemplateName
69         {
70             get { return (string)GetValue(DataTemplateNameProperty); }
71             set { SetValue(DataTemplateNameProperty, value); }
72         }
73
74         public string Text
75         {
76             get { return (string)GetValue(TextProperty); }
77             set { SetValue(TextProperty, value); }
78         }
79
80         public Visibility TextBoxVisibility
81         {
82             get { return (Visibility)GetValue(TextBoxVisibilityProperty); }
83             set { SetValue(TextBoxVisibilityProperty, value); }
84         }
85
86         public bool IsBoxOnly
87         {
88             get;
89             set;
90         }
91
92         public bool OnEnterPressed()
93         {
94             return this.CommitChanges();
95         }
96
97         public void OnEscapePressed()
98         {
99             this.Text = oldText;
100             if (!this.IsBoxOnly)
101             {
102                 this.DataTemplateName = CaseKeyBoxViewModel.LabelTemplate;
103             }
104             this.View.OnEditCancelled();
105         }
106
107         public void OnLabelGotFocus()
108         {
109             this.DataTemplateName = CaseKeyBoxViewModel.BoxesTemplate;
110         }
111
112         public bool OnLostFocus()
113         {
114             return CommitChanges();
115         }
116
117         public void OnValueChanged()
118         {
119             if (this.Value is ModelItem)
120             {
121                 // Since Value is a DP, this code will trigger OnValueChanged once more.
122                 this.Value = ((ModelItem)this.Value).GetCurrentValue();
123                 return;
124             }
125
126             if (this.DataTemplateName != LabelTemplate && !this.IsBoxOnly)
127             {
128                 this.DataTemplateName = LabelTemplate;
129             }
130
131             if (this.DisplayHintText)
132             {
133                 this.Text = string.Empty;
134                 return;
135             }
136             if (this.ValueType == null)
137             {
138                 return;
139             }
140             if (this.ValueType.IsValueType)
141             {
142                 if (this.Value == null)
143                 {
144                     this.Value = Activator.CreateInstance(this.ValueType);
145                 }
146             }
147             if (this.Value == null)
148             {
149                 this.Text = Null;
150             }
151             else if ((this.ValueType == typeof(string)) && string.Equals(this.Value, String.Empty))
152             {
153                 this.Text = Empty;
154             }
155             else
156             {
157                 TypeConverter converter = XamlUtilities.GetConverter(this.ValueType);
158                 Fx.Assert(converter != null, "TypeConverter is not available");
159                 try
160                 {
161                     this.Text = converter.ConvertToString(this.Value);
162                 }
163                 catch (ArgumentException)
164                 {
165                     this.Text = this.Value.ToString();
166                 }
167             }
168         }
169
170         public void OnValueTypeChanged()
171         {
172             if (this.ValueType == null)
173             {
174                 return;
175             }
176             bool isBool = this.ValueType == typeof(bool);
177             bool isEnum = this.ValueType.IsEnum;
178             if (isBool || isEnum)
179             {
180                 this.ComboBoxVisibility = Visibility.Visible;
181                 this.TextBoxVisibility = Visibility.Collapsed;
182                 this.ComboBoxIsEditable = false;
183                 if (isBool)
184                 {
185                     this.ComboBoxItems = new ObservableCollection<string> { "True", "False" };
186                 }
187                 else
188                 {
189                     this.ComboBoxItems = new ObservableCollection<string>(Enum.GetNames(this.ValueType).ToList());
190                 }
191             }
192             else if (this.ValueType.IsValueType)
193             {
194                 this.ComboBoxVisibility = Visibility.Collapsed;
195                 this.TextBoxVisibility = Visibility.Visible;
196                 this.ComboBoxIsEditable = false;
197             }
198             else
199             {
200                 this.ComboBoxVisibility = Visibility.Visible;
201                 this.TextBoxVisibility = Visibility.Collapsed;
202                 this.ComboBoxIsEditable = true;
203                 this.ComboBoxItems = new ObservableCollection<string> { Null };
204                 if (this.ValueType == typeof(string))
205                 {
206                     this.ComboBoxItems.Add(Empty);
207                 }
208             }
209             OnValueChanged();
210         }
211
212         public void SaveOldText()
213         {
214             this.oldText = this.Text;
215         }
216
217         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
218             Justification = "If conversion fails, the exception type is System.Exception.So we must catch all types of exceptions here.")]
219         [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
220             Justification = "Catch all exceptions to prevent crash.")]
221         bool CommitChanges()
222         {
223             object result = null;
224
225             try
226             {
227                 result = ResolveInputText();
228             }
229             catch
230             {
231                 // ---- all
232                 Fx.Assert(false, "Result should have been valid. Preview event handler should have handled the validation.");
233                 return false;
234             }
235
236             this.Value = result;
237             if (this.DataTemplateName != CaseKeyBoxViewModel.LabelTemplate && !this.IsBoxOnly)
238             {
239                 // this is for the case when setting this.Value to null. It looks like
240                 // OnValueChanged won't get called because NULL is a default value for
241                 // the CaseKeyBox instance in SwitchDesigner.
242                 this.DataTemplateName = CaseKeyBoxViewModel.LabelTemplate;
243             }
244             this.View.OnValueCommitted();
245
246             return true;
247         }
248
249         object ResolveInputText()
250         {
251             object result = null;
252             if (this.ValueType == typeof(string))
253             {
254                 if (this.Text.Equals(Null))
255                 {
256                     result = null;
257                 }
258                 else if (this.Text.Equals(Empty))
259                 {
260                     result = string.Empty;
261                 }
262                 else
263                 {
264                     result = this.Text;
265                 }
266             }
267             else if (!this.ValueType.IsValueType && this.Text.Equals(Null))
268             {
269                 result = null;
270             }
271             else
272             {
273                 TypeConverter converter = XamlUtilities.GetConverter(this.ValueType);
274                 Fx.Assert(converter != null, "TypeConverter is not available");
275
276                 if (!converter.CanConvertFrom(typeof(string)) || !converter.CanConvertTo(typeof(string)))
277                 {
278                     throw FxTrace.Exception.AsError(new NotSupportedException(SR.NotSupportedCaseKeyStringConversion));
279                 }
280
281                 result = converter.ConvertFromString(this.Text);
282                 // See if the result can be converted back to a string.
283                 // For example, we have a enum Color {Black, White}.
284                 // String "3" can be converted to integer 3, but integer 3
285                 // cannot be converted back to a valid string for enum Color.
286                 // In this case, we disallow string "3".
287                 converter.ConvertToString(result);
288             }
289
290             string reason;
291             if (this.CaseKeyValidationCallback != null && !this.CaseKeyValidationCallback(result, out reason))
292             {
293                 throw FxTrace.Exception.AsError(new ArgumentException(reason));
294             }
295
296             return result;
297         }
298
299         [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes",
300             Justification = "If conversion fails, the exception type is System.Exception.So we must catch all types of exceptions here.")]
301         [SuppressMessage("Reliability", "Reliability108:IsFatalRule",
302             Justification = "Catch all exceptions to prevent crash.")]
303         public bool CanResolveInputText(out string reason)
304         {
305             reason = string.Empty;
306             try
307             {
308                 ResolveInputText();
309                 return true;
310             }
311             catch (Exception e)
312             {
313                 reason = e.Message;
314                 return false;
315             }
316         }
317
318         public bool TextHasBeenChanged()
319         {
320             string normalizedOldText = this.oldText;
321             string normalizedNewText = this.Text;
322             
323             // Tricky: this.DisplayHintText = false => This CaseKeyBox is in CaseDesigner
324             // Here, when changing value of string value type from "(empty)" to "", we must
325             // consider the text hasn't been changed, such that we don't do commit-change.
326             // We normalize the strings for empty-string situation before we do comparison.
327             if (this.ValueType == typeof(string) && !this.DisplayHintText)
328             {
329                 normalizedOldText = normalizedOldText == Empty ? string.Empty : normalizedOldText;
330                 normalizedNewText = normalizedNewText == Empty ? string.Empty : normalizedNewText;
331             }
332
333             return normalizedOldText != normalizedNewText;
334         }
335
336         ICaseKeyBoxView View { get; set; }
337
338         bool DisplayHintText
339         {
340             get { return this.View.DisplayHintText; }
341         }
342         
343         object Value
344         {
345             get { return this.View.Value; }
346             set { this.View.Value = value; }
347         }
348
349         Type ValueType
350         {
351             get { return this.View.ValueType; }
352         }
353
354         CaseKeyValidationCallbackDelegate CaseKeyValidationCallback
355         {
356             get { return this.View.CaseKeyValidationCallback;  }
357         }
358
359         public void ResetText()
360         {
361             this.Text = string.Empty;
362             this.oldText = string.Empty;
363         }
364     }
365 }