I created a user control with WPF which is supposed to take in a list of controls as its content.
I succeeded in creating such a user control, but have run into the issue that I cannot name any of the sub-controls I added.
Here's the code for the user control:
public static DependencyProperty InnerContentProperty = DependencyProperty.Register("TaskListItems",
typeof(UIElement), typeof(TaskList));
public UIElement TaskListItems {
get { return (UIElement)GetValue(InnerContentProperty); }
set { SetValue(InnerContentProperty, value); }
}
And the XAML (relevant part):
<ScrollViewer>
<ContentControl Content="{Binding TaskListItems, RelativeSource={RelativeSource AncestorType={x:Type local:TaskList}}}"/>
</ScrollViewer>
The implementation then looks like so:
<uc:TaskList Grid.Column="0" HeaderText="Daily Task List" FooterText="Completed: 10">
<uc:TaskList.TaskListItems>
<StackPanel>
<Button Content="Some Button"/>
<Button Content="Some Button 2"/>
</StackPanel>
</uc:TaskList.TaskListItems>
</uc:TaskList>
So when I add a control to my user control such as a button for example, everything works fine, except I cannot give the button a name and I get the error:
Cannot set Name attribute value 'test' on element 'Button'. 'Button' is under the scope of element 'TaskList', which already had a name registered when it was defined in another scope
So my question is, is there a way to functionally do the same thing here, but also letting me give my controls names?
Edit for more information:
What I'm going for is a user control that can take in a list of other user controls I created.
Those "list item" controls have four buttons each (the text bit is also a button):
The text button just opens a window with info about the selected task.
The check mark button is supposed to create an event to request the tasks completion.
The vertical ellipsis button opens a window where you can modify the task.
Lastly the x button is supposed to create an event to request cancellation/removal of the selected task.
Issue lies with creating the events and subscribing to them. What I have so far are two event handlers which are invoked in each of the buttons click events.
public event EventHandler<TaskItemEventArgs> TaskCompletedEvent;
public event EventHandler<TaskItemEventArgs> TaskRemoveRequestEvent;
private void acceptBtn_click(object sender, RoutedEventArgs e)
{
TaskCompletedEvent?.Invoke(this, new TaskItemEventArgs() { EventText = "Task Completed!" });
}
private void removeBtn_click(object sender, RoutedEventArgs e)
{
TaskRemoveRequestEvent?.Invoke(this, new TaskItemEventArgs() { EventText = "Task Remove Requested!" });
}
The problem now is that I don't know where to subscribe these events to in order to modify the "task item" control that invoked the event. I was thinking I should simply add it in the parent control, but I wouldn't know how to go about that, as all the controls here are dynamic.
Now that I think about it, I guess you wouldn't need a control name for the above task. The dynamic controls have me confused though.
I actually got the code to work now after a bit of experimentation and googling. I dynamically create a stack panel in the TaskList control and when I hit the "plus" button in the top right corner, a dialog opens allowing me to describe my task. After that I dynamically create a TaskListItem and subscribe to it's events with some methods I created in TaskList. Finally I add the TaskListItem to the StackPanel and if I want to modify it I can just modify the specifc object that asked to be edited. Works pretty well.
Related
I'm starting my first Universal Windows app. First thing I wanted was to subclass the main "Page" class for navigation. For simple purposes, I wanted to just add a RightTapped event hook to display a message of the actual page displayed...
Anyhow, I created a brand new project. Created a single class MyPage
public class MyPage : Page
{
public MyPage()
{
RightTapped += MyPage_RightTapped;
}
private async void MyPage_RightTapped(object sender, RightTappedRoutedEventArgs e)
{
var dialog = new MessageDialog("This page is " + GetType(), "What is my form");
await dialog.ShowAsync();
}
}
Then on the default main form, I changed MainPage.xaml from
<Page
to
<local:MyPage
In the codebehind, I changed
public sealed partial class MainPage : Page
to
public sealed partial class MainPage
Run the form, it works, right-click on keyboard and message comes up.
Now the problem. In the main page, at the Grid declaration, it is define with a background...
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
If I remove this color (the actual color default is "#FFFFFFFF")
<Grid>
the RightTapped even no longer works and this is the ONLY change. I put in a background of any other color and RightTapped works.
Can anyone explain this and why it fails without background color which should not have any bearing vs working with a background?
This sounds like it's documented behavior. The documentation for UIElement.RightTapped contains some relevant hints:
For touch actions and also for interaction-specific or manipulation events that are consequences of a touch action, an element must be hit-test visible in order to be the event source and fire the event that is associated with the action. UIElement.Visibility must be Visible. Other properties of derived types also affect hit-test visibility. For more info, see Events and routed events overview.
And the details from Events and routed events overview: Hit testing and input events:
There are several factors that affect hit testing, but you can determine whether a given element can fire input events by checking its IsHitTestVisible property. This property returns true only if the element meets these criteria:
The element's Visibility property value is Visible.
The element's Background or Fill property value is not null. A nullBrush value results in transparency and hit test invisibility. (To make an element transparent but also hit testable, use a Transparent brush instead of null.)
The Grid's Background property (inherited from Panel) defaults to null, making the Grid without a Background XAML attribute invisible to hit-testing.
I'm trying to create a visual "designer" that will allow users to drag controls from a toolbox onto a design canvas. The excellent tutorial here has helped me get a basic system up and going - I can drag controls from the toolbox, select and resize them etc. Amongst other things,the code uses a modified Canvas control, overriding the OnDrop method.
However, I'd like to give the user the option of defining "panels" within the design: effectively smaller Canvas's containing the toolbox controls - as an example:
So when the user drags the button onto Canvas_1, OnDrop fires and all is good. However, if the user creates Canvas_2, and then drags the button onto Canvas_2 - itself a child control of Canvas_1 - the parent OnDrop still fires and the button is added to Canvas_1.
I've tried setting the ZIndex of Canvas_2 to greater than Canvas_1, to no avail - the parent Canvas always gets the event. How can I ensure that Canvas_2 gets the OnDrop events for controls landing on it? Is there a different approach I should be using?
The event DragDrop.DragEnter uses a bubbling routing strategy, that means the event will travel upwards in the visual tree starting from the source element, giving a chance for all the elements along the route to handle the event.
When you drop something in the inner Canvas, it handles it, but doesn't stop the event propagation, so the parent element (the containing Canvas) will handle it again.
In the overriden OnDrop method you have a DragEventArgs parameter.
Set its Handled property to true if you want to prevent containing elements to handle the same event.
Example
I created a simple derived Canvas as well:
public class MyCanvas : Canvas
{
public static readonly DependencyProperty StopDropPropagationProperty = DependencyProperty.Register(
"StopDropPropagation", typeof (bool), typeof (MyCanvas), new PropertyMetadata(default(bool)));
public bool StopDropPropagation
{
get { return (bool) GetValue(StopDropPropagationProperty); }
set { SetValue(StopDropPropagationProperty, value); }
}
protected override void OnDrop(DragEventArgs e)
{
Children.Add(new TextBlock
{
Text = "Dropped here"
});
e.Handled = StopDropPropagation;
}
}
In the view I've put two of these, one nested inside the other.
<local:MyCanvas AllowDrop="True" Background="DarkGray">
<local:MyCanvas Width="300" Height="300" Canvas.Left="100" Canvas.Top="10" Background="BurlyWood"
StopDropPropagation="True"></local:MyCanvas>
</local:MyCanvas>
The thing to note is that I added the new DependencyProperty called StopDropPropagation and set it to true in the inner Canvas. That will set the Handled to true.
So when I drop something in the inner Canvas the TextBlock will only be added in that element:
I want to respond to the opening of a bottom app bar in my Windows Store App. Elsewhere, I was advised that there is, indeed, and "Opened" event for it, but this:
<Page.BottomAppBar x:Name="bottomAppBar" Opened="bottomAppBar_Opened" >
...results in a red (unrecognized) "Opened", and the method name I entered does not generate a corresponding handler in the code-behind.
I want to be able to do something like this:
private void bottomAppBar_Opened(object sender, object e)
{
appbarbtnOpenPhotosets.Enabled = PhotraxSQLiteUtils.DatabaseContainsRecords();
appbarbtnCre8FilteredPhotoset.Enabled = appbarbtnOpenPhotosets.IsEnabled; // or call the
query again, if necessary
appbarbtnClearMap.Enabled = MapHasMarkers();
}
...but what event can I use, or how can I hook into the opening of the appbar?
And actually, giving the Page's BottomAppBar a name was not allowed, either.
Page.TopAppBar is an (attached) property, so you are trying to set a property on a property assignment. Moreover, as you see here, the child of Page.TopAppBar must be an AppBar object.
So, you should do the following
<Page.TopAppBar>
<AppBar Opened="...">
<!-- Here the AppBar's content -->
</AppBar>
</Page.TopAppBar>
Hai
am having a WPF user control in my WPF Form, when i click my button in my form , i just want to pass some value to the textbox which is in the usercontrol, tell me how to do this.
There are several ways you can do this. The easiest way is to use a String property and implement INotifyPropertyChanged in your UserControl.
To illustrate, you will have your UserControl like so:
/// <summary>
/// Interaction logic for TextBoxUsercontrol.xaml
/// </summary>
public partial class TextBoxUsercontrol : UserControl, INotifyPropertyChanged
{
private string _text;
public string Text
{
get { return _text; }
set
{
_text = value;
if(PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Text"));
}
}
public TextBoxUsercontrol()
{
DataContext = this;
InitializeComponent();
}
public event PropertyChangedEventHandler PropertyChanged;
}
Now the TextBox in your UserControl must bind itself to your Text property like so:
<TextBox Text="{Binding Text}" />
Then in your WPF form, you will have your UserControl declared and a button to handle the click like so:
<local:TextBoxUsercontrol x:Name="textBox" />
<Button Click="ButtonBase_OnClick" >Add Text</Button>
And finally, in your Click handler:
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
textBox.Text = "Hello!";
}
Having shown you the solution, I give your question asking skill a 1 out of 5. You can be a lot more specific with your question and give sample codes snippets as I have done without asking us to download your whole solution from a site that we must wait download it (not to mention most of us are security conscious about downloading unknown files).
Good luck.
Standard WPF? NO WAY.
Why? You dont pass values around. You get a click event on the item that is clicked (the button) with elements defined on the button (only), then in the code you access the other elements, which thus also have to be either defined in code (the standard way) and expose their values through something called "properties", OR you get the control by name and extract the value. But you dont pass any additional data around.
Look at the tutorials ;)
If you want to PASS values around to METHODS on a click, you need to use something like caliburn (http://www.codeplex.com/caliburn) which allows you to map the click to a method and grab the values passed into the method from other controls.
Just Create a Dependency property and Bind the Porperty to the UserControl's TextBox. While creating the object itself assign the value to the Usercontrol's dependency property.
I've got a WPF application.
On the left side there is a stackpanel full of buttons.
On the right side there is an empty dockpanel.
When user clicks a button, it loads the corresponding UserControl (View) into the dockpanel:
private void btnGeneralClick(object sender, System.Windows.RoutedEventArgs e)
{
PanelMainContent.Children.Clear();
Button button = (Button)e.OriginalSource;
Type type = this.GetType();
Assembly assembly = type.Assembly;
IBaseView userControl = UserControls[button.Tag.ToString()] as IBaseView;
userControl.SetDataContext();
PanelMainContent.Children.Add(userControl as UserControl);
}
This pattern works well since each UserControl is a View which has a ViewModel class which feeds it information which it gets from the Model, so the user can click from page to page and each page can carry out isolated functionality, such as editing all customers, saving to the database, etc.
Problem:
However, now, on one of these pages I want to have a ListBox with a list of Customers in it, and each customer has an "edit" button, and when that edit button is clicked, I want to fill the DockPanel with the EditSingleCustomer UserControl and pass it the Customer that it needs to edit.
I can load the EditCustomer usercontrol, but how do I pass it the customer to edit and set up its DataContext to edit that customer?
I can't pass it in the constructor since all the UserControls are already created and exist in a Dictionary in the MainWindow.xaml.cs.
so I created a PrepareUserControl method on each UserControl and pass the Customer to it and can display it with a textbox from code behind with x:Name="..." but that is not the point, I need to DataBind an ItemsControl to a ObservableCollection to take advantage of WPF's databinding functionality of course.
so I tried to bind the ListBox ItemSource in the View to its code behind like this:
<UserControl.Resources>
<local:ManageSingleCustomer x:Key="CustomersDataProvider"/>
</UserControl.Resources>
<DockPanel>
<ListBox ItemsSource="{Binding Path=CurrentCustomersBeingEdited, Source={StaticResource CustomersDataProvider}}"
ItemTemplate="{DynamicResource allCustomersDataTemplate}"
Style="{DynamicResource allCustomersListBox}">
</ListBox>
</DockPanel>
which gets a stackoverflow error caused by an endless loop in the IntializeComponent() in that view. So I'm thinking I'm going about this in the wrong way, there must be some easier paradigm to simply pass commands from one UserControl to another UserControl in WPF (and before someone says "use WPF commanding", I already am using commanding on my UserControl that allows the user to edit all customers, which works fine, but I have to handle it in my code behind of my view (instead of in my viewmodel) since I need the parent window context to be able to load another user control when its finished saving:
<Button Style="{StaticResource formButton}"
Content="Save"
Command="local:Commands.SaveCustomer"
CommandParameter="{Binding}"/>
private void OnSave(object sender, System.Windows.Input.ExecutedRoutedEventArgs e)
{
Customer customer = e.Parameter as Customer;
Customer.Save(customer);
MainWindow parentShell = Window.GetWindow(this) as MainWindow;
Button btnCustomers = parentShell.FindName("btnCustomers") as Button;
btnCustomers.RaiseEvent(new RoutedEventArgs(Button.ClickEvent));
}
So how in WPF can I simply have a UserControl loaded in a DockPanel, inside that UserControl a button with a command on it that loads another UserControl and sends that UserControl a specific object to which it can bind its controls?
I can imagine I just do not know enough about WPF commands at this point, if anyone can point me in the right direction from here, that would be great, or if you think this "loading UserControls in a DockPanel pattern is foreign to WPF and should be avoided and replaced with another way to structure applications", that would be helpful news as well. You can download the current state of my application here to get an idea of how it is structured. Thanks.
I've just finished a LOB application using WPF where this sort of problem/pattern appeared constantly, so here's how I would have solved your problem:
1) In the DataTemplate where you create each item in the ListBox, along with it's edit button, bind the Button's tag property to the Customer object underlying that list box item.
2) Create a Click event handler for the button, and set the Button's Click event to fire the handler.
3) In the event handler, set the Content property of the UserControl.
4) Set up a DataTemplate in scope of the User Control (perhaps in the resources of it's immediate container) which describes an editor for that single customer.
Another approach that will work is to declare a Customer dependency property on your EditCustomer class, then set that property (perhaps through a XAML Trigger) when the button is clicked.
I hope this isn't too vague. If nothing else, know that the problem you're facing is very solvable in WPF.
This is where you use the Mediator pattern. There's several blog posts on this topic (for instance), and there's implementations of the pattern in some WPF frameworks (such as EventAggregator in Prism).
I don't have the time to really dig into this (it's an interesting question and I hope you get a good answer-- I can see myself running into a similar situation in the future).
Have you considered getting a little less WPF-y and falling back to firing an event on your source UserControl with an EventArgs that contains the customer, then in the event handler, firing the appropriate command on the target control?