In WPF(.Net 4.5) I have RichTextBox in my xaml
<RichTextBox Height="40" Width="100">
<FlowDocument>
<Paragraph>
<Run Text="{Binding TestRichTextBox}"/>
</Paragraph>
</FlowDocument>
</RichTextBox>
in and ViewModel I have this Property
string testRichTextBox;
public string TestRichTextBox
{
get { return testRichTextBox; }
set
{
if (testRichTextBox == value)
return;
testRichTextBox = value;
onPropertyChanged("TestRichTextBox");
}
}
scenario :
RichTextBox has content
select all text (ctrl+A)
paste new text
in ViewModel I have breakpoint on set method, new Value is empty string.
Is this normal behavior or a bug?
Could you please try this and check if var text is returning any value?
public Window1()
{
InitializeComponent();
// "tb" is a richtextBox
DataObject.AddPastingHandler(tb, OnPaste);
}
private void OnPaste(object sender, DataObjectPastingEventArgs e)
{
var isText = e.SourceDataObject.GetDataPresent(DataFormats.UnicodeText, true);
if (!isText) return;
var text = e.SourceDataObject.GetData(DataFormats.UnicodeText) as string;
...
}
Related
I have a button and a popup control in it in my xaml as follows:
<Button Grid.Column="0" Grid.Row="5"
HorizontalAlignment="Center"
Margin="88,8,214,0"
Grid.RowSpan="2" Height="26"
VerticalAlignment="Top" Width="22"
IsEnabled="{Binding Path=SearchFound}"
x:Name="cmdPlanList"
Click="cmdPlanList_Click">
<ContentControl>
<Popup IsOpen = "{Binding PlanPopup}"
PlacementTarget = "{Binding ElementName = cmdPlanList}"
AllowsTransparency = "True"
PopupAnimation = "Slide"
x:Name="Popup4Plan">
<Canvas Width = "125"
Height = "100"
Background = "Red" >
<Canvas.RenderTransform>
<RotateTransform x:Name = "theTransform" />
</Canvas.RenderTransform>
<TextBlock TextWrapping = "Wrap"
Foreground = "Blue"
Text = "Hi, this is Popup" />
</Canvas>
</Popup>
</ContentControl>
</Button>
I am setting the DataContext of this Popup from my code-behind as follows:-
My View's code behind:-
using xyz
{
private bool _PlanPopup = false;
public bool PlanPopup
{
get { return _PlanPopup; }
set
{
_PlanPopup = value;
}
}
public MyView()
{
InitializeComponent();
Popup4Plan.DataContext = this;
}
private void cmdPlanList_Click(object sender, RoutedEventArgs e)
{
this.PlanPopup = this.PlanPopup ? false : true;
}
}
If you want to bind the View to a property of itself, make the property a dependency property.
public bool IsOpen
{
get
{
return (bool)GetValue(IsOpenProperty);
}
set
{
SetValue(IsOpenProperty, value);
}
}
public static readonly DependencyProperty IsOpenProperty =
DependencyProperty.Register("IsOpen", typeof(bool), typeof(MyView), new PropertyMetadata(false));
To quickly make it type propdp [tab][tab] and fill in the blanks.
Also:
this.PlanPopup = this.PlanPopup ? false : true;
looks much better this way:
this.PlanPopup = !this.PlanPopup;
For updates on bound properties to work you need to implement INotifyPropertyChanged. For example like the following.
public class XYZ : INotifyPropertyChanged
{
private bool isOpen;
public bool IsOpen {
get { return this.isOpen; }
set {
this.isOpen = value;
this.OnPropertyChanged();
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
With this code an instance of XYZ will notify that the property IsOpen has changed and any bound view elements will re-fetch the value of IsOpen.
Say I have a TextBlock that looks like this:
<TextBlock TextWrapping="Wrap" >
Text in <Run Foreground="Red">red</Run> and <Run Foreground="Red">more</Run>
</TextBlock>
That works fine and the text is shown in red as you think it would. However, in my real example this text:
Text in <Run Foreground="Red">red</Run> and <Run Foreground="Red">more</Run>
Comes from a service call. (Including the tags.)
Is there a way to supply this text to the TextBlock at runtime?
In fact you have to convert all the input text into Inlines collection (of type TextElementCollection). However doing so requires parsing the input text yourself which is not easy. So why not use the existing parser supported by XamlReader. By using this class, you can parse a XAML code to get an instance of a TextBlock from which you can get the Inlines collection and add that for your actual TextBlock. Here is the code:
var text = "<TextBlock>Text in <Run Foreground=\"Red\">red</Run> and <Run Foreground=\"Red\">more</Run></TextBlock>";
var pc = new System.Windows.Markup.ParserContext();
pc.XmlnsDictionary[""] = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
pc.XmlnsDictionary["x"] = "http://schemas.microsoft.com/winfx/2006/xaml";
var tbl = System.Windows.Markup.XamlReader.Parse(text, pc) as TextBlock;
yourTextBlock.Inlines.Clear();
yourTextBlock.Inlines.AddRange(tbl.Inlines.ToList());
Your actual input text should be wrapped in the pair <TextBlock> & </TextBlock> before being used by the next code.
I thought I would post the control that I made from King Kong's answer. (Just in case it is useful to someone else.)
public class FormatBindableTextBlock : TextBlock
{
private static readonly ParserContext parserContext;
static FormatBindableTextBlock()
{
parserContext = new System.Windows.Markup.ParserContext();
parserContext.XmlnsDictionary[""] = "http://schemas.microsoft.com/winfx/2006/xaml/presentation";
parserContext.XmlnsDictionary["x"] = "http://schemas.microsoft.com/winfx/2006/xaml";
}
public static readonly DependencyProperty FormattedContentProperty =
DependencyProperty.Register("FormattedContent", typeof(string), typeof(FormatBindableTextBlock), new UIPropertyMetadata(null, OnFormattedContentPropertyChanged));
public string FormattedContent
{
get { return (string)GetValue(FormattedContentProperty); }
set { SetValue(FormattedContentProperty, value); }
}
private static void OnFormattedContentPropertyChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
FormatBindableTextBlock textBlock = sender as FormatBindableTextBlock;
if (textBlock == null)
return;
string newFormattedContent = e.NewValue as string;
if (string.IsNullOrEmpty(newFormattedContent))
newFormattedContent = "";
newFormattedContent = "<TextBlock>" + newFormattedContent + "</TextBlock>";
TextBlock tbl = System.Windows.Markup.XamlReader.Parse(newFormattedContent, parserContext) as TextBlock;
textBlock.Inlines.Clear();
textBlock.Inlines.AddRange(tbl.Inlines.ToList());
}
}
I'm trying to build a search field using the AutoCompleteBox from the WPF Toolkit. The AutoCompleteBox's Text property is bound to a property in a ViewModel that implements INotifyPropertyChanged. When the property is changed, it fetches new suggestions to show to the user.
This gets mucked up if the user uses arrow keys to scan through the list of autocomplete suggestions before choosing one - the moment the cursor move into the popup, SelectionChanged is fired, the text field gets a new value, and the autocomplete suggestions are re-collected. This also interferes with my desire to use the SelectionChanged event to kick off a search.
Is there any way to prevent the SelectionChanged event from firing on keyboard navigation?
Here's how I have things set up. Note sc:SearchField is a subclass of AutoCompleteBox that only provides a way to access the TextBox property on the AutoCompleteBox so I can call functions like SelectAll()
XAML:
<sc:SearchField x:Name="SearchField" DataContext="{Binding SearchBoxVm}" Text="{Binding Query, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" ItemsSource="{Binding QuerySuggestions, UpdateSourceTrigger=PropertyChanged}" SelectedItem="{Binding SelectedItem, Mode=TwoWay}" IsTextCompletionEnabled="False" Margin="54,10,117,67" Grid.RowSpan="2" BorderThickness="0" FontSize="14" PreviewKeyUp="searchField_OnKeyup" Foreground="{Binding Foreground, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" FontStyle="{Binding QueryFont, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" >
</sc:SearchField>
ViewModel:
void GetQuerySuggestions()
{
if (!string.IsNullOrEmpty(Query) && !Query.Equals(DEFAULT_TEXT))
{
QueryFont = FontStyles.Normal;
Foreground = Brushes.Black;
QuerySuggestions = SearchAssistant.GetQueryRecommendations(_query);
}
}
public string _query = DEFAULT_TEXT;
public string Query
{
get
{
return _query;
}
set
{
_query = value;
GetQuerySuggestions();
NotifyPropertyChanged("Query");
}
}
List<string> querySuggestions = new List<string>();
public List<string> QuerySuggestions
{
get { return querySuggestions; }
set
{
querySuggestions = value;
NotifyPropertyChanged("QuerySuggestions");
}
}
SearchField subclass:
public class SearchField : AutoCompleteBox
{
public TextBox TextBox
{
get
{
return (this.GetTemplateChild("Text") as TextBox);
}
}
}
Not sure if this is what you are wanting to do but I have the following code which only changes the selection when the 'Enter' key is pressed or the mouse is used to select an item from the list (left mouse button clicked). I can arrow up and down the list without issue and only fire the selection changed event when the user presses enter or clicks on the desired entry.
Note that I am using the AutoCompleteBox and not the SearchField as you are using.
In XAML:
<toolkit:AutoCompleteBox Name="OmniSearchTextBox"
ItemsSource="{Binding CompanyList}"
SelectedItem="{Binding SelectedObject, Mode=TwoWay}"
IsTextCompletionEnabled="False"
FilterMode="Contains"
KeyUp="OmniSearch_KeyUp"
MouseLeftButtonUp="OmniSearch_MouseLeftButtonUp"
Margin="10,94,10,0"
RenderTransformOrigin="0.518,1.92" Height="35"
VerticalAlignment="Top" />
In code behind:
private void OmniSearch_KeyUp(object sender, KeyEventArgs e)
{
if (e.Key == Key.Enter)
{
BindingExpression exp = this.OmniSearchTextBox.GetBindingExpression(AutoCompleteBox.SelectedItemProperty);
exp.UpdateSource();
}
}
private void OmniSearch_MouseLeftButtonUp(object sender, MouseEventArgs e)
{
BindingExpression exp = this.OmniSearchTextBox.GetBindingExpression(AutoCompleteBox.SelectedItemProperty);
exp.UpdateSource();
}
In ViewModel:
private const string CompanyListPropertyName = "CompanyList";
private ObservableCollection<Company> _companyList;
public ObservableCollection<Company> CompanyList
{
get
{
return _companyList;
}
set
{
if (_companyList == value)
{
return;
}
_companyList = value;
RaisePropertyChanged(CompanyListPropertyName);
}
}
private Company _selectedObject;
public Company SelectedObject
{
get
{
return _selectedObject;
}
set
{
if (_selectedObject != value)
{
_selectedObject = value;
}
}
}
I'm using Silverlight on Windows Phone 7.
I want to display the first part of some text in a TextBlock in bold, and the rest in normal font. The complete text must wrap. I want the bolded part to contain text from one property in my ViewModel, and the plain text to contain text from a different property.
The TextBlock is defined in a DataTemplate associated with a LongListSelector.
My initial attempt was:
<TextBlock TextWrapping="Wrap">
<TextBlock.Inlines>
<Run Text="{Binding Property1}" FontWeight="Bold"/>
<Run Text="{Binding Property2}"/>
</TextBlock.Inlines>
</TextBlock>
This fails at runtime with the spectacularly unhelpful "AG_E_RUNTIME_MANAGED_UNKNOWN_ERROR". This is a known issue because the Run element is not a FrameworkElement and cannot be bound.
My next attempt was to put placeholders in place, and then update them in code:
<TextBlock Loaded="TextBlockLoaded" TextWrapping="Wrap">
<TextBlock.Inlines>
<Run FontWeight="Bold">Placeholder1</Run>
<Run>Placeholder2</Run>
</TextBlock.Inlines>
</TextBlock>
In the code-behind (yes I am desparate!):
private void TextBlockLoaded(object sender, RoutedEventArgs e)
{
var textBlock = (TextBlock)sender;
var viewModel = (ViewModel)textBlock.DataContext;
var prop1Run = (Run)textBlock.Inlines[0];
var prop2Run = (Run)textBlock.Inlines[1];
prop1Run.Text = viewModel.Property1;
prop2Run.Text = viewModel.Property2;
}
This seemed to work, but because I am using the LongListSelector, although items get recycled, the Loaded codebehind event handler doesn't re-initialize the Runs, so very quickly the wrong text is displayed...
I've looked at using the LongListSelector's Linked event (which I already use to free up images that I display in the list), but I can't see how I can use that to re-initialize the Runs' text properties.
Any help appreciated!
I finally found a solution that works for me.
As I mention in the comment, Paul Stovell's approach would not work.
Instead I used a similar approach to add an attached property to the TextBlock, bound to the TextBlock's DataContext, and attached properties on the runs, indicating which ViewModel properties they should be bound to:
<TextBlock TextWrapping="Wrap"
Views:BindableRuns.Target="{Binding}">
<TextBlock.Inlines>
<Run FontWeight="Bold" Views:BindableRuns.Target="Property1"/>
<Run Views:BindableRuns.Target="Property2"/>
</TextBlock.Inlines>
</TextBlock>
Then in my attached TextBox Target (datacontext) property's changed event, I update the Runs, and subscribe to be notified of changes to the TextBox Target properties. When a TextBox Target property changes, I updated any associated Run's text accordingly.
public static class BindableRuns
{
private static readonly Dictionary<INotifyPropertyChanged, PropertyChangedHandler>
Handlers = new Dictionary<INotifyPropertyChanged, PropertyChangedHandler>();
private static void TargetPropertyPropertyChanged(
DependencyObject dependencyObject,
DependencyPropertyChangedEventArgs e)
{
if(!(dependencyObject is TextBlock)) return;
var textBlock = (TextBlock)dependencyObject;
AddHandler(e.NewValue as INotifyPropertyChanged, textBlock);
RemoveHandler(e.OldValue as INotifyPropertyChanged);
InitializeRuns(textBlock, e.NewValue);
}
private static void AddHandler(INotifyPropertyChanged dataContext,
TextBlock textBlock)
{
if (dataContext == null) return;
var propertyChangedHandler = new PropertyChangedHandler(textBlock);
dataContext.PropertyChanged += propertyChangedHandler.PropertyChanged;
Handlers[dataContext] = propertyChangedHandler;
}
private static void RemoveHandler(INotifyPropertyChanged dataContext)
{
if (dataContext == null || !Handlers.ContainsKey(dataContext)) return;
dataContext.PropertyChanged -= Handlers[dataContext].PropertyChanged;
Handlers.Remove(dataContext);
}
private static void InitializeRuns(TextBlock textBlock, object dataContext)
{
if (dataContext == null) return;
var runs = from run in textBlock.Inlines.OfType<Run>()
let propertyName = (string)run.GetValue(TargetProperty)
where propertyName != null
select new { Run = run, PropertyName = propertyName };
foreach (var run in runs)
{
var property = dataContext.GetType().GetProperty(run.PropertyName);
run.Run.Text = (string)property.GetValue(dataContext, null);
}
}
private class PropertyChangedHandler
{
private readonly TextBlock _textBlock;
public PropertyChangedHandler(TextBlock textBlock)
{
_textBlock = textBlock;
}
public void PropertyChanged(object sender,
PropertyChangedEventArgs propertyChangedArgs)
{
var propertyName = propertyChangedArgs.PropertyName;
var run = _textBlock.Inlines.OfType<Run>()
.Where(r => (string) r.GetValue(TargetProperty) == propertyName)
.SingleOrDefault();
if(run == null) return;
var property = sender.GetType().GetProperty(propertyName);
run.Text = (string)property.GetValue(sender, null);
}
}
public static object GetTarget(DependencyObject obj)
{
return obj.GetValue(TargetProperty);
}
public static void SetTarget(DependencyObject obj,
object value)
{
obj.SetValue(TargetProperty, value);
}
public static readonly DependencyProperty TargetProperty =
DependencyProperty.RegisterAttached("Target",
typeof(object),
typeof(BindableRuns),
new PropertyMetadata(null,
TargetPropertyPropertyChanged));
}
I suggest you give the BindableRun a try. I've only used it in WPF, but I don't see why it wouldn't work in Silverlight.
I need to create a Dialog / Prompt including TextBox for user input. My problem is, how to get the text after having confirmed the dialog? Usually I would make a class for this which would save the text in a property. However I want do design the Dialog using XAML. So I would somehow have to extent the XAML Code to save the content of the TextBox in a property - but I guess that's not possible with pure XAML. What would be the best way to realize what I'd like to do? How to build a dialog which can be defined from XAML but can still somehow return the input? Thanks for any hint!
The "responsible" answer would be for me to suggest building a ViewModel for the dialog and use two-way databinding on the TextBox so that the ViewModel had some "ResponseText" property or what not. This is easy enough to do but probably overkill.
The pragmatic answer would be to just give your text box an x:Name so that it becomes a member and expose the text as a property in your code behind class like so:
<!-- Incredibly simplified XAML -->
<Window x:Class="MyDialog">
<StackPanel>
<TextBlock Text="Enter some text" />
<TextBox x:Name="ResponseTextBox" />
<Button Content="OK" Click="OKButton_Click" />
</StackPanel>
</Window>
Then in your code behind...
partial class MyDialog : Window {
public MyDialog() {
InitializeComponent();
}
public string ResponseText {
get { return ResponseTextBox.Text; }
set { ResponseTextBox.Text = value; }
}
private void OKButton_Click(object sender, System.Windows.RoutedEventArgs e)
{
DialogResult = true;
}
}
Then to use it...
var dialog = new MyDialog();
if (dialog.ShowDialog() == true) {
MessageBox.Show("You said: " + dialog.ResponseText);
}
Edit: Can be installed with nuget https://www.nuget.org/packages/PromptDialog/
I just add a static method to call it like a MessageBox:
<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
x:Class="utils.PromptDialog"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
WindowStartupLocation="CenterScreen"
SizeToContent="WidthAndHeight"
MinWidth="300"
MinHeight="100"
WindowStyle="SingleBorderWindow"
ResizeMode="CanMinimize">
<StackPanel Margin="5">
<TextBlock Name="txtQuestion" Margin="5"/>
<TextBox Name="txtResponse" Margin="5"/>
<PasswordBox Name="txtPasswordResponse" />
<StackPanel Orientation="Horizontal" Margin="5" HorizontalAlignment="Right">
<Button Content="_Ok" IsDefault="True" Margin="5" Name="btnOk" Click="btnOk_Click" />
<Button Content="_Cancel" IsCancel="True" Margin="5" Name="btnCancel" Click="btnCancel_Click" />
</StackPanel>
</StackPanel>
</Window>
And the code behind:
public partial class PromptDialog : Window
{
public enum InputType
{
Text,
Password
}
private InputType _inputType = InputType.Text;
public PromptDialog(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
InitializeComponent();
this.Loaded += new RoutedEventHandler(PromptDialog_Loaded);
txtQuestion.Text = question;
Title = title;
txtResponse.Text = defaultValue;
_inputType = inputType;
if (_inputType == InputType.Password)
txtResponse.Visibility = Visibility.Collapsed;
else
txtPasswordResponse.Visibility = Visibility.Collapsed;
}
void PromptDialog_Loaded(object sender, RoutedEventArgs e)
{
if (_inputType == InputType.Password)
txtPasswordResponse.Focus();
else
txtResponse.Focus();
}
public static string Prompt(string question, string title, string defaultValue = "", InputType inputType = InputType.Text)
{
PromptDialog inst = new PromptDialog(question, title, defaultValue, inputType);
inst.ShowDialog();
if (inst.DialogResult == true)
return inst.ResponseText;
return null;
}
public string ResponseText
{
get
{
if (_inputType == InputType.Password)
return txtPasswordResponse.Password;
else
return txtResponse.Text;
}
}
private void btnOk_Click(object sender, RoutedEventArgs e)
{
DialogResult = true;
Close();
}
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
So you can call it like:
string repeatPassword = PromptDialog.Prompt("Repeat password", "Password confirm", inputType: PromptDialog.InputType.Password);
Great answer of Josh, all credit to him, I slightly modified it to this however:
MyDialog Xaml
<StackPanel Margin="5,5,5,5">
<TextBlock Name="TitleTextBox" Margin="0,0,0,10" />
<TextBox Name="InputTextBox" Padding="3,3,3,3" />
<Grid Margin="0,10,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Button Name="BtnOk" Content="OK" Grid.Column="0" Margin="0,0,5,0" Padding="8" Click="BtnOk_Click" />
<Button Name="BtnCancel" Content="Cancel" Grid.Column="1" Margin="5,0,0,0" Padding="8" Click="BtnCancel_Click" />
</Grid>
</StackPanel>
MyDialog Code Behind
public MyDialog()
{
InitializeComponent();
}
public MyDialog(string title,string input)
{
InitializeComponent();
TitleText = title;
InputText = input;
}
public string TitleText
{
get { return TitleTextBox.Text; }
set { TitleTextBox.Text = value; }
}
public string InputText
{
get { return InputTextBox.Text; }
set { InputTextBox.Text = value; }
}
public bool Canceled { get; set; }
private void BtnCancel_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = true;
Close();
}
private void BtnOk_Click(object sender, System.Windows.RoutedEventArgs e)
{
Canceled = false;
Close();
}
And call it somewhere else
var dialog = new MyDialog("test", "hello");
dialog.Show();
dialog.Closing += (sender,e) =>
{
var d = sender as MyDialog;
if(!d.Canceled)
MessageBox.Show(d.InputText);
}
You don't need ANY of these other fancy answers. Below is a simplistic example that doesn't have all the Margin, Height, Width properties set in the XAML, but should be enough to show how to get this done at a basic level.
XAML
Build a Window page like you would normally and add your fields to it, say a Label and TextBox control inside a StackPanel:
<StackPanel Orientation="Horizontal">
<Label Name="lblUser" Content="User Name:" />
<TextBox Name="txtUser" />
</StackPanel>
Then create a standard Button for Submission ("OK" or "Submit") and a "Cancel" button if you like:
<StackPanel Orientation="Horizontal">
<Button Name="btnSubmit" Click="btnSubmit_Click" Content="Submit" />
<Button Name="btnCancel" Click="btnCancel_Click" Content="Cancel" />
</StackPanel>
Code-Behind
You'll add the Click event handler functions in the code-behind, but when you go there, first, declare a public variable where you will store your textbox value:
public static string strUserName = String.Empty;
Then, for the event handler functions (right-click the Click function on the button XAML, select "Go To Definition", it will create it for you), you need a check to see if your box is empty. You store it in your variable if it is not, and close your window:
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
Calling It From Another Page
You're thinking, if I close my window with that this.Close() up there, my value is gone, right? NO!! I found this out from another site: http://www.dreamincode.net/forums/topic/359208-wpf-how-to-make-simple-popup-window-for-input/
They had a similar example to this (I cleaned it up a bit) of how to open your Window from another and retrieve the values:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Cancel Button
You're thinking, well what about that Cancel button, though? So we just add another public variable back in our pop-up window code-behind:
public static bool cancelled = false;
And let's include our btnCancel_Click event handler, and make one change to btnSubmit_Click:
private void btnCancel_Click(object sender, RoutedEventArgs e)
{
cancelled = true;
strUserName = String.Empty;
this.Close();
}
private void btnSubmit_Click(object sender, RoutedEventArgs e)
{
if (!String.IsNullOrEmpty(txtUser.Text))
{
strUserName = txtUser.Text;
cancelled = false; // <-- I add this in here, just in case
this.Close();
}
else
MessageBox.Show("Must provide a user name in the textbox.");
}
And then we just read that variable in our MainWindow btnOpenPopup_Click event:
private void btnOpenPopup_Click(object sender, RoutedEventArgs e)
{
MyPopupWindow popup = new MyPopupWindow(); // this is the class of your other page
//ShowDialog means you can't focus the parent window, only the popup
popup.ShowDialog(); //execution will block here in this method until the popup closes
// **Here we find out if we cancelled or not**
if (popup.cancelled == true)
return;
else
{
string result = popup.strUserName;
UserNameTextBlock.Text = result; // should show what was input on the other page
}
}
Long response, but I wanted to show how easy this is using public static variables. No DialogResult, no returning values, nothing. Just open the window, store your values with the button events in the pop-up window, then retrieve them afterwards in the main window function.