I have a simple UserControl that I want to display in my stackpanel programmatically
.
When I do, the UC does not show on the screen. If I drag a single instance from the toolbox it works fine.
The User Control is XAML is
<UserControl x:Class="MYProj.Controls.SpecialNumberOption"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="200">
<Viewbox>
<Grid x:Name="LayoutRoot" Background="White" Width="200" Height="300">
<Button x:Name="buttonMe" Content="Button" HorizontalAlignment="Left" Margin="10,10,0,0" VerticalAlignment="Top" Width="180" Height="150" Style="{StaticResource NumberButtonStyle}" Click="buttonMe_Click"/>
<TextBlock x:Name="subText" HorizontalAlignment="Left" Margin="10,165,0,0" TextWrapping="Wrap" Text="TextBlock" VerticalAlignment="Top" Height="125" Width="180" FontStyle="Italic" TextAlignment="Center"/>
</Grid>
</Viewbox>
</UserControl>
The Codebehind
public partial class SpecialNumberOption : UserControl
{
public event RoutedEventHandler Click;
public SpecialNumberOption()
{
InitializeComponent();
this.applyStyle();
}
public SpecialNumberOption(SurveyQuestionOption option)
{
this.buttonMe.Content = option.Text;
this.subText.Text = option.SubText;
this.applyStyle();
}
private void applyStyle()
{
this.buttonMe.FontSize = 26;
this.buttonMe.Background = standardBackground;
this.buttonMe.Foreground = standardForecolor;
}
///Raise the event to the outside
private void buttonMe_Click(object sender, RoutedEventArgs e)
{
Click(this, e);
}
}
Implementation
This is how Im adding the control
foreach (var y in x.Options)
{
//Create new instance from An object
var r = new SpecialNumberOption(y);
// Set visibility
r.Visibility = System.Windows.Visibility.Visible;
r.IsEnabled = false;
//Assign the event handler
r.Click += r_Click;
//This is my stackpanel
listOptions.Children.Add(r);
....
}
//Handle the click event
void r_Click(object sender, RoutedEventArgs e)
{
SpecialNumberOption o = (SpecialNumberOption)e.OriginalSource;
....
}
Update
I found when I use the alternate Constructor this is when it ceases to work.
I have to use the default constructor. Is this normal?
I have not checked all of your posted code for correctness but here is the issue with your constructors: you must call InitializeComponent (and it must happen before you access any named elements)
here is a version with a fix:
public SpecialNumberOption()
{
InitializeComponent();
this.applyStyle();
}
public SpecialNumberOption(SurveyQuestionOption option) : this () //will call the empty default constructor
{
this.buttonMe.Content = option.Text;
this.subText.Text = option.SubText;
}
Remark: I consider it bad style for controls to have more than the empty default constructor.
The parameters should be set via a property setter. It enable you and whoever will be reusing your control to use and parameterize it in xaml.
Related
My window architecture is a Mainwindow.xaml which contains many usercontrols ie: Pesos.xaml and Materiales.xaml. The problem is when the user clicks over any "Materiales.xaml" component, the focused TextBox element in "Pesos.xaml" loses the focus.
I tried to add a public property which returns the Textbox of "Pesos" usercontrol about im interested on with no success.
By the moment is enaugh for me that when the keyup event be throwed by the "Materiales" usercontrol, the focus goes to "Pesos" usercomponent TextBox. This is Pesos.cs.xaml, at first you can see BuscadorTexto public property in order to access it from out of the usercontrol. Secondly, I have the UserControl_GotFocus() which it will throw an exception when the focus is received from Materiales user control due to the DataContext is null:
public partial class Pesos : UserControl
{
public Pesos()
{
InitializeComponent();
}
bool txtPesoFocus = false;
public TextBox BuscadorTexto {
get { return this.txtBuscador; }
}
...
private void UserControl_GotFocus(object sender, RoutedEventArgs e)
{
if (!((ViewModel.PuestoViewModel)DataContext).IsMaterialValido.GetValueOrDefault())
this.txtBuscador.Focus();
else if (((ViewModel.PuestoViewModel)DataContext).IsMaterialValido.GetValueOrDefault()
&& ((ViewModel.PuestoViewModel)DataContext).SelectedBascula.Tipo == Comun.TipoMaquina.Manual)
txtPeso.Focus();
}
}
And this is Materiales.cs:
<UserControl x:Class="A99.AsuaProducts.Ensayos.Controles.Puestos.OfMateriales"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:A99.AsuaProducts.Ensayos.Controles.Puestos"
xmlns:iconPacks="http://metro.mahapps.com/winfx/xaml/iconpacks"
xmlns:controls="http://metro.mahapps.com/winfx/xaml/controls"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800" >
<Grid>
<Grid.RowDefinitions>
...
</Grid.RowDefinitions>
<Grid HorizontalAlignment="Stretch">
<Grid.ColumnDefinitions>
...
</Grid.ColumnDefinitions>
</Grid>
<DataGrid x:Name="MetroDataGrid"
ItemsSource="{Binding ListaOfIteracionMaterialesMaquina}"
SelectedItem="{Binding SelectedOfIteracionMaterial}"
Grid.Row="1"
Margin="2"
AutoGenerateColumns="False"
HeadersVisibility="All"
CanUserAddRows="False"
CanUserDeleteRows="False"
CanUserReorderColumns="False"
CanUserResizeRows="False"
CanUserSortColumns="False"
CanUserResizeColumns="False"
IsReadOnly="True"
SelectionUnit="FullRow"
Focusable="False"
KeyUp="MetroDataGrid_KeyUp">
<DataGrid.Columns>
...
</DataGrid.Columns>
</DataGrid>
...
</UserControl>
This is the Materiales code behind:
public partial class OfMateriales : UserControl
{
public OfMateriales()
{
InitializeComponent();
}
private void Button_GotFocus(object sender, RoutedEventArgs e)
{
MessageBox.Show("Ojo que siguiente tiene el foco");
}
private void MetroDataGrid_KeyUp(object sender, KeyEventArgs e)
{
Pesos uc = new Pesos();
uc.BuscadorTexto.Text = "KK";
uc.BuscadorTexto.Focus();
}
}
As you can see, I tried to edit the textBox content and set the focus but no effect at all.
You have to create a new focus scope targeting the Materiales control. This way focusing child elements of the Materiales control won't steal the keyboard focus from the TextBox (or any other control inside the main focus scope) anymore.
You use the FocusManager to create a new focus scope:
MainWindow.xaml
<Window>
<StackPanel>
<TextBox />
<!--
A separate focus scope will prevent the user control
from stealing focus of any element inside the MainWindow focus scope
-->
<Materiales FocusManager.IsFocusScope="True" />
</StackPanel>
</Window>
I have a MainWindowViewModel and my MainWindow contains a frame to display project pages.
The first page being displayed is a list of recently opened projects(Similar to Microsoft word) which has it's own ViewModel.
There is no problem in loading the list but when I want to send the user-selected item from this list to the MainWindowViewModel I can not use Find-Ancestor to reach the Window DataContext(It looks like the frame has some restrictions).
How can I send the user-selected item to the MainWindowViewModel?
public class RecentlyOpenedFilesViewModel
{
readonly IFileHistoryService _fileHistoryService;
private ObservableCollection<RecentlyOpenedFileInfo> _RecentlyOpenedFilesList;
public ObservableCollection<RecentlyOpenedFileInfo> RecentlyOpenedFilesList
{
get { return _RecentlyOpenedFilesList; }
set { _RecentlyOpenedFilesList = value; RaisePropertyChanged(); }
}
public RecentlyOpenedFilesViewModel( IFileHistoryService fileService):base()
{
_fileHistoryService = fileService;
RecentlyOpenedFilesList=new ObservableCollection<RecentlyOpenedFileInfo>(_fileHistoryService.GetFileHistory());
}
public void RefreshList()
{
RecentlyOpenedFilesList = new ObservableCollection<RecentlyOpenedFileInfo>(_fileHistoryService.GetFileHistory());
}
}
<Page
x:Class="MyProject.Views.V3.Other.RecentlyOpenedFilesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyProject.Views.V3.Other"
xmlns:vmv3="clr-namespace:MyProject"
Title="RecentlyOpenedFilesPage">
<Page.Resources>
<DataTemplate x:Key="RecentlyOpenedFileInfoTemplate"
>
<Button
Height="70"
Command="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ListView}}, Path=DataContext.OpenProjectFromPathCommand}"
CommandParameter="{Binding}">
<Button.Content>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="70" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="200" />
</Grid.ColumnDefinitions>
<StackPanel
Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Top">
<TextBlock Text="{Binding Name}" />
<TextBlock Text="{Binding Path}" />
</StackPanel>
<TextBlock
Grid.Row="0"
Grid.Column="1"
Margin="50,0,0,0"
VerticalAlignment="Center"
Text="{Binding DateModified}" />
</Grid>
</Button.Content>
</Button>
</DataTemplate>
</Page.Resources>
<Grid>
<ListView
ItemTemplate="{StaticResource RecentlyOpenedFileInfoTemplate}"
ItemsSource="{Binding RecentlyOpenedFilesList}" />
</Grid>
public RecentlyOpenedFilesPage(MainWindowViewModel vm)
{
this.DataContext = vm;
InitializeComponent();
}
Now I have a direct link between MainWindowViewModel and RecentlyOpenedFilesViewModel but I would like to remove this dependency and use another way of connection like(routed commands which I have a problem with)
The MainWindow contains a frame in which the RecentlyOpenedFilesPage is set to its content.
<Window
x:Class="MyProject.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:fw="clr-namespace:SourceChord.FluentWPF;assembly=FluentWPF" >
<Frame Name="frameMain"/></Window>
public class MainWindowViewModel : RecentlyOpenedFilesViewModel, IMainWindowViewModel
{
private void LoadRecentlyOpenedProjects()
{
CurrentView = new RecentlyOpenedFilesPage(this);
}
}
So, here is my suggested solution. It uses the basic idea to propagate the DataContext from the outside into a frame content, as presented in page.DataContext not inherited from parent Frame?
For demonstration purpose, I provide an UI with a button to load the page, a textblock to display the selected result from the list within the page and (ofcourse) the frame that holds the page.
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Grid Name="parentGrid">
<TextBlock VerticalAlignment="Top" HorizontalAlignment="Right" Margin="5" Text="{Binding SelectedFile}" Width="150" Background="Yellow"/>
<Button VerticalAlignment="Top" HorizontalAlignment="Left" Margin="5" Click="Button_Click" Width="150">Recent Files List</Button>
<Frame Name="frameMain" Margin="5 50 5 5"
LoadCompleted="frame_LoadCompleted"
DataContextChanged="frame_DataContextChanged"/>
</Grid>
</Window>
Viewmodel classes:
public class BaseVm : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged([CallerMemberName]string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
public class MyWindowVm : BaseVm
{
private string _selectedFile;
public string SelectedFile
{
get => _selectedFile;
set
{
_selectedFile = value;
OnPropertyChanged();
}
}
}
public class MyPageVm : BaseVm
{
public ObservableCollection<MyRecentFile> Files { get; } = new ObservableCollection<MyRecentFile>();
}
public class MyRecentFile
{
public string Filename { get; set; }
public string FilePath { get; set; }
}
Main code behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
parentGrid.DataContext = new MyWindowVm();
}
// Load Page on some event
private void Button_Click(object sender, RoutedEventArgs e)
{
frameMain.Content = new RecentlyOpenedFilesPage(new MyPageVm
{
Files =
{
new MyRecentFile { Filename = "Test1.txt", FilePath = "FullPath/Test1.txt"},
new MyRecentFile { Filename = "Test2.txt", FilePath = "FullPath/Test2.txt"}
}
});
}
// DataContext to Frame Content propagation
private void frame_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
UpdateFrameDataContext(sender as Frame);
}
private void frame_LoadCompleted(object sender, NavigationEventArgs e)
{
UpdateFrameDataContext(sender as Frame);
}
private void UpdateFrameDataContext(Frame frame)
{
var content = frame.Content as FrameworkElement;
if (content == null)
return;
content.DataContext = frame.DataContext;
}
}
Now, the page.xaml ... notice: we will set the page viewmodel to the pageRoot.DataContext, not to the page itself. Instead we expect the page datacontext to be handled from the outside (as we do in the MainWindow) and we can reference it with the page internal name _self:
<Page x:Class="WpfApplication1.RecentlyOpenedFilesPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Title="RecentlyOpenedFilesPage"
Name="_self">
<Grid Name="pageRoot">
<ListView ItemsSource="{Binding Files}"
SelectedValue="{Binding DataContext.SelectedFile,ElementName=_self}"
SelectedValuePath="FilePath"
DisplayMemberPath="Filename"/>
</Grid>
</Page>
Page code behind to wire up the viewmodel:
public partial class RecentlyOpenedFilesPage : Page
{
public RecentlyOpenedFilesPage(MyPageVm myPageVm)
{
InitializeComponent();
pageRoot.DataContext = myPageVm;
}
}
As you can see, with this setup, no viewmodel knows about any involved view. The page doesn't handle the MainViewmodel, but the page requires a DataContext with a SelectedFile property to be provided from the outside.
The MainViewmodel doesn't know about the recent file list, but allows to set a selected file, no matter where it originates from.
The decision, to initialize the RecentlyOpenedFilesPage with a pre-created viewmodel is not important. You could just as well use internal logic to initialize the page with recent files, then the Mainwindow would not be involved.
What I'm attempting to do:
Create a custom page control that consumers can use just like the UWP page, but, that also displays it's own custom content along side the consumers content.
What have I tried:
Creating a new Control, inheriting from Page
Creating a templated control that inherits from page
Creating a control that contains a page
Setting the ContentProperty attribute and binding to it in my custom page
What is the problem?
When I attempt to create a control that has both a xaml and xaml.cs file, that inherits from Page I get InvalidCastExceptions on random controls inside the subclassed control.
Example:
TestPage.xaml
<Page
x:Class="ControlSandbox.TestPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:uwp_toolkit="using:Microsoft.Toolkit.Uwp.UI.Controls"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Page.Content>
<ContentPresenter Content="{Binding}" />
</Page.Content>
<Page.BottomAppBar>
<AppBar Background="Transparent" x:Name="appbar" IsOpen="True">
<uwp_toolkit:InAppNotification x:Name="note">
<StackPanel>
<TextBlock>HEADER!</TextBlock>
<TextBlock>Message</TextBlock>
<StackPanel Orientation="Horizontal">
<Button>OK</Button>
<Button>Cancel?</Button>
</StackPanel>
</StackPanel>
</uwp_toolkit:InAppNotification>
</AppBar>
</Page.BottomAppBar>
</Page>
TestPage.xaml.cs
public partial class TestPage : Page
{
public TestPage()
{
this.InitializeComponent();
}
public void ShowNotification()
{
appbar.IsOpen = true;
note.Show();
}
}
MainPage.xaml
<controlsandbox:TestPage
xmlns:controlsandbox="using:ControlSandbox" x:Class="ControlSandbox.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid>
<Button Click="Button_Click">SHOW NOTIFICATION</Button>
</Grid>
</controlsandbox:TestPage>
MainPage.xaml.cs
public partial class MainPage : TestPage
{
public MainPage()
{
this.InitializeComponent();
}
private void Button_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
this.ShowNotification();
}
}
The above code results in an InvalidCastException and for the life of me I can't find the problem.
System.InvalidCastException: 'Unable to cast object of type
'Windows.UI.Xaml.Controls.AppBar' to type
'Windows.UI.Xaml.Controls.Button'.'
Now if I do the same exact code, but all in the MainPage.xaml instead of in the TestPage.xaml everything works as expected
Update
So I believe this is a bug in the platform. Here is a demo I did of the issue. Please prove me wrong because this would be a real limitation https://github.com/DotNetRussell/UWP_Page_Inheritance_Bug
Update
I added the changes for the answer below. It seems that when I create a normal templated control and put it on a vanilla uwp page, it works fine. However, when I create a templated Page, it ignores my template.
Update
I think this is a bug in the platform. I opened an issue up on github https://github.com/microsoft/microsoft-ui-xaml/issues/1075
The problem is that if create custom base page with Xaml, it will be mandatory converted to the subpage's content, if the sub-page contained controls different with Base page will throw exception. And the better way is create base class without xaml and add the base page content in the code behind. For more code please refer the following .
public class BasePage : Page
{
protected override void OnNavigatedTo(NavigationEventArgs e)
{
this.BottomAppBar = new AppBar()
{
Background = new SolidColorBrush(Colors.Transparent),
IsOpen = false,
Content = new InAppNotification
{
Content = new StackPanel
{
Children =
{
new TextBlock{ Text = "HEADER!"},
new TextBlock{Text = "Message"},
new StackPanel
{
Orientation = Orientation.Horizontal,
Children=
{
new Button{Content = "ok"},
new Button {Content = "cancel"}
}
}
}
}
}
};
base.OnNavigatedTo(e);
}
public void ShowNotification()
{
this.BottomAppBar.IsOpen = true;
InAppNotification note = this.BottomAppBar.Content as InAppNotification;
if (note != null)
note.Show();
}
}
Usage
public sealed partial class MainPage : BasePage
{
public MainPage()
{
this.InitializeComponent();
}
private void Button_Click(object sender, RoutedEventArgs e)
{
ShowNotification();
}
}
Xaml
<local:BasePage
x:Class="CustomPage.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="using:CustomPage"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
>
<Grid>
<Button Click="Button_Click" Content="ClikMe" />
</Grid>
</local:BasePage>
Create a Templated Control (Project->Add New Item->Templated Control in Visual Studio):
public sealed class CustomPage : Page
{
private AppBar appbar;
private InAppNotification note;
public CustomPage()
{
this.DefaultStyleKey = typeof(CustomPage);
}
protected override void OnApplyTemplate()
{
appbar = GetTemplateChild("appbar") as AppBar;
note = GetTemplateChild("note") as InAppNotification;
}
public void ShowNotification()
{
if (appbar != null)
appbar.IsOpen = true;
note?.Show();
}
}
...and define a custom Style in Themes/Generic.xaml:
<Style TargetType="local:CustomPage">
<Setter Property="BottomAppBar">
<Setter.Value>
<AppBar Background="Transparent" x:Name="appbar" IsOpen="True">
<uwp_toolkit:InAppNotification x:Name="note">
<StackPanel>
<TextBlock>HEADER!</TextBlock>
<TextBlock>Message</TextBlock>
<StackPanel Orientation="Horizontal">
<Button>OK</Button>
<Button>Cancel?</Button>
</StackPanel>
</StackPanel>
</uwp_toolkit:InAppNotification>
</AppBar>
</Setter.Value>
</Setter>
</Style>
There is .xaml.cs file for the CustomPage base class.
Edit: Since the BottomAppBar is not part of the template, you need to wait to access the elements in it until they have actually been created. Just do this in the method:
public sealed class CustomPage : Page
{
public CustomPage()
{
this.DefaultStyleKey = typeof(CustomPage);
}
public void ShowNotification()
{
AppBar appBar = this.BottomAppBar;
appBar.IsOpen = true;
InAppNotification note = appBar.Content as InAppNotification;
if(note != null)
note.Show();
}
}
I'm making a basic program where a label updates when the user types in a text box. i'm trying to use data binding and INotifyPropertyChanged to work this out, so i don't want any workarounds. i used 2 buttons so i can actually see if they updated. here's my main class
namespace TestStringChangeFromAnotherClass
public partial class MainWindow : Window
{
textClass someTextClass = new textClass();
public MainWindow()
{
InitializeComponent();
}
public string someString1;
public string someString2;
private void btn1_Click(object sender, RoutedEventArgs e)
{
someTextClass.Text1 = tbx1.Text;
}
private void btn2_Click(object sender, RoutedEventArgs e)
{
someTextClass.Text2 = tbx1.Text;
}
}
here's the wpf for it
<Window x:Class="TestStringChangeFromAnotherClass.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
DataContext="{Binding RelativeSource={RelativeSource Self}}"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button x:Name="btn1" Content="Button" HorizontalAlignment="Left" Height="36" Margin="29,246,0,0" VerticalAlignment="Top" Width="108" Click="btn1_Click"/>
<Button x:Name="btn2" Content="Button" HorizontalAlignment="Left" Height="36" Margin="227,246,0,0" VerticalAlignment="Top" Width="124" Click="btn2_Click"/>
<Label x:Name="lbl1" Content="{Binding textClass.Text1}" HorizontalAlignment="Left" Height="37" Margin="74,32,0,0" VerticalAlignment="Top" Width="153"/>
<Label x:Name="lbl2" Content="{Binding textClass.Text2, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Left" Height="38" Margin="74,90,0,0" VerticalAlignment="Top" Width="153"/>
<TextBox x:Name="tbx1" HorizontalAlignment="Left" Height="37" Margin="290,32,0,0" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="190"/>
</Grid>
as you can see, i've tried using UpdateSourceTrigger. i've also tried to use "someTestClass.Text1" instead of textClass.Test1, because that's how i defined it in the MainWindow. Here's my textClass
namespace TestStringChangeFromAnotherClass
public class textClass:INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string text1;
public string Text1
{
get { return text1; }
set
{
text1 = value;
NotifyPropertyChanged("Text1");
}
}
private string text2;
public string Text2
{
get { return text2; }
set
{
text2 = value;
NotifyPropertyChanged("Text2");
}
}
protected void NotifyPropertyChanged(string info)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(info));
}
}
i can't figure out how to get wpf to look for the Test1 or Test2 strings in the separate class and update them when the strings change. i have a feeling the problem lies within DataContext, but i can't figure it out. i'd also rather not use DataContext within c#, only in WPF
UPDATE:
when i debug this, when it gets to NotifyPropertyChanged, PropertyChanged is evaluated as null. could that be the problem?
You bind DataContext to your Window which, as far as I can see, doesn't have textClass property. It has someTextClass field of textClass type. In order for your code to work your can change someTextClass to public property:
public textClass someTextClass { get; private set; }
initialize it in constructor:
public MainWindow()
{
someTextClass = new textClass();
InitializeComponent();
}
and then change binding to point to someTextClass property
<Label x:Name="lbl1" Content="{Binding someTextClass.Text1}" .../>
<Label x:Name="lbl2" Content="{Binding someTextClass.Text2}" .../>
You are binding to the MainWindow class itself as your DataContext, and trying to access the property called someTextClass that has the properties you want to bind to.
You are running into two problems:
1) Your XAML is trying to reference the desired object by it's type, not it's name. Not going to work. Your binding expressions should look like {Binding someTextClass.Text1} (note the difference in the first part of the path expression).
2) You can only bind to public things. Your field is not defined as public, and therefore is private. Even though the XAML should logically "be able to see" the property, as it's the same class, DataBinding will only work on public properties.
3) EDIT: You must also make this a property. WPF will not bind to fields.
In general, using Snoop will help diagnose silent binding errors.
Is it possible to change sender's property in event?
I have my own control in wpf with 10 Image controls.
I set on all of them mouse enter and mouse leave events.
All those events do the same(change size and Z index) but for specific Image.
With changing sender's property in event I will have only 2 event's methods, not 20.
When I tried to change sender's property I saw it was readonly.
Is it possible to do ?
Point all your controls at the same handlers. You can do this at design time or through code.
In the handler cast sender to the type of control.
Now when you change it's properties, you are changing the properties of the control that raised the event
PS don't forget to check to see if the cast is valid, before you try to access it's members.
Here how you can do it in xaml
/ <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void image1_MouseEnter(object sender, MouseEventArgs e)
{
//put your code here and all your images will points here
}
private void image1_MouseLeave(object sender, MouseEventArgs e)
{
//put your code here and all your images will points here
}
}
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Image Height="51" HorizontalAlignment="Left" Margin="91,116,0,0" Name="image1" Stretch="Fill" VerticalAlignment="Top" Width="61" MouseEnter="image1_MouseEnter" MouseLeave="image1_MouseLeave" />
<Image Height="51" HorizontalAlignment="Left" Margin="91,116,0,0" Name="image2" Stretch="Fill" VerticalAlignment="Top" Width="61" MouseEnter="image1_MouseEnter" MouseLeave="image1_MouseLeave" />
<Image Height="51" HorizontalAlignment="Left" Margin="91,116,0,0" Name="image3" Stretch="Fill" VerticalAlignment="Top" Width="61" MouseEnter="image1_MouseEnter" MouseLeave="image1_MouseLeave" />
<Image Height="51" HorizontalAlignment="Left" Margin="91,116,0,0" Name="image4" Stretch="Fill" VerticalAlignment="Top" Width="61" MouseEnter="image1_MouseEnter" MouseLeave="image1_MouseLeave" />
</Grid>
</Window>
//and here in code behind
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
image1.MouseEnter += image1_MouseEnter;
image2.MouseEnter += image1_MouseEnter;
image3.MouseEnter += image1_MouseEnter;
image4.MouseEnter += image1_MouseEnter;
image1.MouseEnter += image1_MouseLeave;
image2.MouseEnter += image1_MouseLeave;
image3.MouseEnter += image1_MouseLeave;
image4.MouseEnter += image1_MouseLeave;
}
private void image1_MouseEnter(object sender, MouseEventArgs e)
{
}
private void image1_MouseLeave(object sender, MouseEventArgs e)
{
}
}