I want to get only one number and one circle when I click the button so if I click the button 3 times I will get the 3 numbers with the circles obviously. It's suppose to be lottery numbers appearing after every click but I have no idea how can I do that.
private void btnGo_Click(object sender, RoutedEventArgs e)
{
Ellipse first = new Ellipse();
first.Fill = new SolidColorBrush(Colors.Red);
first.Height = 70;
first.Width = 70;
first.Margin = new Thickness(50, 100, 0, 0);
caPaper.Children.Add(first);
Ellipse second = new Ellipse();
second.Fill = new SolidColorBrush(Colors.Red);
second.Height = 70;
second.Width = 70;
second.Margin = new Thickness(150, 100, 0, 0);
caPaper.Children.Add(second);
Ellipse third = new Ellipse();
third.Fill = new SolidColorBrush(Colors.Red);
third.Height = 70;
third.Width = 70;
third.Margin = new Thickness(250, 100, 0, 0);
caPaper.Children.Add(third);
Random rd = new Random();
TextBlock txt1 = new TextBlock();
txt1.FontSize = 20;
txt1.Foreground = Brushes.White;
txt1.Text = " " + rd.Next(1, 45);
Canvas.SetTop(txt1, 120);
Canvas.SetLeft(txt1, 70);
caPaper.Children.Add(txt1);
TextBlock txt2 = new TextBlock();
txt2.FontSize = 20;
txt2.Foreground = Brushes.White;
txt2.Text = " " + rd.Next(1, 45);
Canvas.SetTop(txt2, 120);
Canvas.SetLeft(txt2, 170);
caPaper.Children.Add(txt2);
TextBlock txt3 = new TextBlock();
txt3.FontSize = 20;
txt3.Foreground = Brushes.White;
txt3.Text = " " + rd.Next(1, 45);
Canvas.SetTop(txt3, 120);
Canvas.SetLeft(txt3, 270);
caPaper.Children.Add(txt3);
}
private void btnClear_Click(object sender, RoutedEventArgs e)
{
caPaper.Children.Clear();
This is 100% the wrong approach to this.
You should never be dynamically creating UI controls like this. It's so wrong its not worth figuring out what is wrong in your code.
Use MVVM, it will make your life much, much, much easier. Your lottery numbers are data, the balls, a visual representation of that data. So in your VM:
public ObservableCollection<int> Draws {get;} = new ObservableCollection<int>();
// Invoked from command, or could be a click handler until you learn commands
public AddDraw()
{
int draw = GetNextDraw(); //Left as an exercise
Draws.Add(draw)
}
Now that the data is handled, we need to display it. An ItemsControl is how you display collections, use the ItemTemplate to control how the number is displayed (the ball) and ItemsPanel to control the layout (a horizontal WrapPanel is probably what you want):
<ItemsControl ItemsSource="{Binding Draws}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<Grid>
<Ellipse Fill="Red/>
<TextBlock Text="{Binding Path=.}" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</DataTemplate>
</ItemsControl.ItemTemplate>
<ItemsControl.ItemsPanel>
<ItemPanelTemplate>
<WrapPanel Orientation="Horizontal"/>
</ItemPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
That's off the top of my head so I may have a property or class wrong. Intellisense should correct it pretty quickly though.
Once its set up you can add margin, change the colors, whatever. This design is what WPF was built for, take advantage of it.
Disclaimer: I have had to deal with creating UI controls at all ONCE. In 7 years of professional WPF work, and only because the data structure being represented was incredibly complex (and not similar to how it was being represented).
As mentioned below, your approach could use a bit of tweaking. WPF has some really powerful data binding features to build UI MUCH MORE pain free. However to answer your question...
It looks as though the only thing that is 'unique' per ball is the margin of the ball and the left value in Canvas.SetLeft
You should be able to derive those values based on the number of children in your canvas. For example something like this:
int marginLeft = 50 + (caPaper.Children.Length * 100);
Ellipse newBall = new Ellipse();
newBall.Fill = new SolidColorBrush(Colors.Red);
newBall.Height = 70;
newBall.Width = 70;
newBall.Margin = new Thickness(marginLeft, 100, 0, 0);
caPaper.Children.Add(newBall);
You'll want to adjust that of course but I wanted to give you an idea to work with. Also, the same will need to be done for the text. Then, each time you click it, it will create them in relation to the number of children it already has.
Related
In my UWP App I've got a grid with 2 columns. The App is adaptive and on the mobile I only want to show one column at a time. Is there a way to use animations to reduce the width from column 1 and expand the width from column 2 and the other way round?
Animating size and layout has always been tricky in XAML frameworks. Why? Not because you cannot animate a Width, you can, but the performance usually sucks as a change to the Width/Height automatically triggers layout updates which then do a lot of re-calculating, re-measuring and re-arranging stuff on the UI thread that hurts the performance.
But there's always some workarounds you can do. With Windows Composition API, it's now a lot easier to animate layout changes while maintaining 60 frames per second fresh rate, all thanks to the new API such as ImplicitAnimations, SetImplicitHideAnimation & SetImplicitShowAnimation.
ImplicitAnimations basically allows you to monitor property changes like Opacity, Offset, Size, etc and whenever they are updated, the old value will be animated to the new value smoothly; where SetImplicitHideAnimation & SetImplicitShowAnimation will simply animate when the Visibility of an element is changed. So instead of disappearing instantly, one element can scale down and fade out.
Note you will need to provide your desired animations for the APIs to know how to animate. To make your life a bit easier, I have created some helper methods (see link at the bottom) that encapsulates some key animations that you generally need.
To find out exactly what they do, take a look at the gif below
I am re-positioning, hiding and showing elements in different adaptive visual states, no animation is written in XAML, but with the following code, the Composition API simply takes care of animating all these changes implicitly.
var compositor = this.Visual().Compositor;
// Create background visuals.
var leftBackgroundVisual = compositor.CreateSpriteVisual();
leftBackgroundVisual.Brush = compositor.CreateColorBrush(Colors.Crimson);
LeftGridBackgroundVisualWrapper.SetChildVisual(leftBackgroundVisual);
var middleBackgroundVisual = compositor.CreateSpriteVisual();
middleBackgroundVisual.Brush = compositor.CreateColorBrush(Colors.Gold);
MiddleGridBackgroundVisualWrapper.SetChildVisual(middleBackgroundVisual);
var rightBackgroundVisual = compositor.CreateSpriteVisual();
rightBackgroundVisual.Brush = compositor.CreateColorBrush(Colors.DarkOrchid);
RightGridBackgroundVisualWrapper.SetChildVisual(rightBackgroundVisual);
// Sync background visual dimensions.
LeftGridBackgroundVisualWrapper.SizeChanged += (s, e) => leftBackgroundVisual.Size = e.NewSize.ToVector2();
MiddleGridBackgroundVisualWrapper.SizeChanged += (s, e) => middleBackgroundVisual.Size = e.NewSize.ToVector2();
RightGridBackgroundVisualWrapper.SizeChanged += (s, e) => rightBackgroundVisual.Size = e.NewSize.ToVector2();
// Enable implilcit Offset and Size animations.
LeftText.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
MiddleText.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
RightText.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
LeftGrid.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
MiddleGrid.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
RightGrid.EnableImplicitAnimation(VisualPropertyType.Offset, 400);
leftBackgroundVisual.EnableImplicitAnimation(VisualPropertyType.Size, 400);
middleBackgroundVisual.EnableImplicitAnimation(VisualPropertyType.Size, 400);
rightBackgroundVisual.EnableImplicitAnimation(VisualPropertyType.Size, 400);
// Enable implicit Visible/Collapsed animations.
LeftGrid.EnableFluidVisibilityAnimation(showFromScale: 0.6f, hideToScale: 0.8f, showDuration: 400, hideDuration: 250);
MiddleGrid.EnableFluidVisibilityAnimation(showFromScale: 0.6f, hideToScale: 0.8f, showDelay: 200, showDuration: 400, hideDuration: 250);
RightGrid.EnableFluidVisibilityAnimation(showFromScale: 0.6f, hideToScale: 0.8f, showDelay: 400, showDuration: 400, hideDuration: 250);
There's quite a lot of code so I am not posting everything here. But feel free to check it out from this link.
You can use bind to do it. And you should make two property in Page that code is below.
public static readonly DependencyProperty RcProperty = DependencyProperty.Register(
"Rc", typeof(double), typeof(MainPage), new PropertyMetadata(100d));
public double Rc
{
get { return (double) GetValue(RcProperty); }
set { SetValue(RcProperty, value); }
}
public static readonly DependencyProperty LcProperty = DependencyProperty.Register(
"Lc", typeof(double), typeof(MainPage), new PropertyMetadata(500d));
public double Lc
{
get { return (double) GetValue(LcProperty); }
set { SetValue(LcProperty, value); }
}
But we cant bind double to GridLength that we should add a convert.
public class DoubletoGridConvert : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, string language)
{
var n = (double) value;
return new GridLength(n);
}
public object ConvertBack(object value, Type targetType, object parameter, string language)
{
throw new NotImplementedException();
}
}
After we wrote it, we can make the Page like below.
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="{x:Bind Rc,Mode=OneWay,Converter={StaticResource double}}"/>
<RowDefinition Height="{x:Bind Lc,Mode=OneWay,Converter={StaticResource double}}"/>
</Grid.RowDefinitions>
<Grid Background="#FF565656"></Grid>
<Grid Grid.Row="1" Background="#FFa2a2a2"></Grid>
</Grid>
<Button Margin="47,662,0,10" Content="set" Click="Button_OnClick"></Button>
We do the animation when button clicked.
private void Button_OnClick(object sender, RoutedEventArgs e)
{
this.Name = nameof(MainPage);
var storyboard = new Storyboard();
var animation = new DoubleAnimation();
Storyboard.SetTargetName(animation, nameof(MainPage));
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetProperty(animation,"Rc");
animation.EnableDependentAnimation = true;
animation.From = 100;
animation.To = 200;
animation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
storyboard.Children.Add(animation);
storyboard.Begin();
storyboard = new Storyboard();
animation = new DoubleAnimation();
Storyboard.SetTarget(animation, this);
Storyboard.SetTargetName(animation,nameof(MainPage));
Storyboard.SetTargetProperty(animation, nameof(Lc));
animation.From = 500;
animation.To = 150;
animation.Duration = new Duration(TimeSpan.FromMilliseconds(500));
animation.EnableDependentAnimation = true;
storyboard.Children.Add(animation);
storyboard.Begin();
}
I think it can help you.
I am trying to draw an ellipse using the Path control dynamically.
In my MainWindow():
EllipseGeometry ellipse = new EllipseGeometry(new Point(50, 50), 45, 20);
var path = new Path();
path.VerticalAlignment = VerticalAlignment.Top;
path.HorizontalAlignment = HorizontalAlignment.Left;
path.Fill = Brushes.Black;
path.Stroke = new SolidColorBrush(Colors.Green);
path.StrokeThickness = 2;
path.Data = ellipse;
but nothing shows up.
I realised that I need to "associate" the path object with my dialog box but I do not know how to do that. Is there a way to accomplish this via non-XAML methods since I will need to dynamically generate many different path objects?
All you are missing is basically that:
SamplePanel.Children.Add(path);
The above assumes that there is a Panel named SamplePanel in your window's XAML, e.g.
<Grid x:Name="SamplePanel" />
I'm trying to make a custom hamburger menu for my app. I have the layout all setup, but, for some reason, my DoubleAnimation isn't working as I expect. I'm trying to accomplish something similar to Cortana's hamburger menu on Windows 10. I'm creating my Storyboard and DoubleAnimation in my code (I still have the same problem even if my animation is created in XAML). Here's what I have:
menuButton.Click += (s, e) => {
if (menuIsOpen) {
Animate(
menuPanel,
"Width",
ActualWidth, //Page.ActualWidth
50,
0.1,
new Duration(new TimeSpan(TimeSpan.TicksPerSecond)));
}
else {
Animate(
menuPanel,
"Width",
50,
ActualWidth, //Page.ActualWidth
0.1,
new Duration(new TimeSpan(TimeSpan.TicksPerSecond)));
}
menuIsOpen = !menuIsOpen;
};
This is the signature for Animate():
private void Animate(DependencyObject item, string property, double from, double to, double? by, Duration duration) {
var storyboard = new Storyboard();
var animation = new DoubleAnimation();
animation.From = from;
animation.To = to;
animation.Duration = duration;
animation.EasingFunction = new SineEase() { EasingMode = EasingMode.EaseInOut };
animation.By = by;
Storyboard.SetTarget(animation, item);
Storyboard.SetTargetProperty(animation, property);
storyboard.Children.Add(animation);
storyboard.Begin();
}
I used a little Debug.WriteLine() to see if the Storyboard is actually running, which it is. The menuPanel's width does not change at all. I've used a similar approach with some of my other projects and it works fine there.
EDIT:
I thought I'd add a little bit more detail.
My app is targeting Windows 10
and I'm able to change the width of menuPanel freely if I don't use Storyboards and DoubleAnimations.
I found this answer on a similar question, and my animation works just fine now.
I figured it out myself. All I had to do was to Enable Dependent Animation (EnableDependentAnimation) on the DoubleAnimation as this animation affects the layout. And then it worked perfectly.
I want to add an array of grids to my WPF window:
Grid[] Tiles = new Grid[20];
public void LoadTile()
{
for (int X = 0; X < Tiles.Length; X++)
{
Tiles[X] = new Grid();
Tiles[X].Height = (TileData[X].SizeY * 90) - 10;
Tiles[X].Width = (TileData[X].SizeY * 90) - 10;
Tiles[X].Margin = new Thickness(0 + (TileData[X].PositionX * 90), 216 + (TileData[X].PositionY * 90), 0, 0);
Tiles[X].HorizontalAlignment = System.Windows.HorizontalAlignment.Center;
Tiles[X].VerticalAlignment = System.Windows.VerticalAlignment.Center;
Tiles[X].Visibility = System.Windows.Visibility.Visible;
SolidColorBrush Brush1 = new SolidColorBrush(Colors.Black);
Brush1.Opacity = 0.2;
Tiles[X].Background = Brush1;
}
}
That's what I have.
(BTW: I do have a method calling that one I just didn't include it here)
I added:
Nine_Window.Content = Tiles[X];
But it made it so all I could display was one of them, because each time the loop did that piece of code again it overwrote the last one
Your usual use for a grid (let's assume 3x3) will look something along the following in the XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
</Grid>
Regarding your problem with setting the Content, You are setting it to a specific tile instead of to the array. But Again, it'll be easy to do from the XAML I believe, and simply initialize it from code if you need to.
I think what you are actually looking for is row and column definitions of a grid. Add as many as you need by executing:
Grid Tile = new Grid()
// create new columns
ColumDefintion columnDefinition = new ColumnDefinition()
columnDefinition.Height = ... // set height here
Tile.ColumnDefinitions.Add(columnDefinition);
// create a row
Tile.RowDefinitions.Add(new RowDefinition());
Otherwise your changes will affect the whole grid object.
Well I donot second your approach but if you want to continue with it, do not add your Grids like this
Nine_Window.Content = Tiles[X];
instead add a stackPanel to NineWindow.Content
<Window ....>
<Grid>
<StackPanel x:Name="myStackPanel"></StackPanel>
</Grid>
and then in code behind
myStackPanel.Children.Add(Tile[X]);
Okay, Muds nearly got my answer but I'm gonna use a Canvas instead of a Stack Panel.
If you didn't get what I meant, it's simple, I wanted to create a multiple grid controls in an array and add them to my window.
I cannot get ActualWidth of a canvas to any nonzero value. I simplified the real scenario and devoted a new WPF Application in VS to getting a nonzero value (and understanding). Unfortunately, I'm only getting zero. I feel like I'm missing some basic WPF understanding: I'm not that familiar with WPF.
I copied the MDSN demo, modified it slightly and I now have
public partial class MainWindow : Window {
private Canvas myCanvas;
public MainWindow()
{
InitializeComponent();
CreateAndShowMainWindow();
}
private void CreateAndShowMainWindow()
{
// Create a canvas sized to fill the window
myCanvas = new Canvas();
myCanvas.Background = Brushes.LightSteelBlue;
// Add a "Hello World!" text element to the Canvas
TextBlock txt1 = new TextBlock();
txt1.FontSize = 14;
txt1.Text = "Hello World!";
Canvas.SetTop(txt1, 100);
Canvas.SetLeft(txt1, 10);
myCanvas.Children.Add(txt1);
// Add a second text element to show how absolute positioning works in a Canvas
TextBlock txt2 = new TextBlock();
txt2.FontSize = 22;
txt2.Text = "Isn't absolute positioning handy?";
Canvas.SetTop(txt2, 200);
Canvas.SetLeft(txt2, 75);
myCanvas.Children.Add(txt2);
Grid content = new Grid();
content.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
content.Children.Add(myCanvas);
this.Content = content;
this.Loaded += MainWindow_Loaded;
}
void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var canvasRenderWidth = myCanvas.RenderSize.Width;
var canvasActualWidth = myCanvas.ActualWidth;
} //place breakpoint here
I expect the canvas to have the same ActualWidth as the textbox after loading, but at the specified breakpoint, it's zero. Notice that the textboxes are visible when running the code above.
Can someone tell me how to make myCanvas.ActualWidth to automatically become the textbox.ActualWidth or tell me why this shouldn't be done?
In my real usage scenario I've got a Canvas in a in column of a Grid, where the columndefinition's width is set to auto, so I expected it to increase as the canvas' width increases. However, this fails, and I suspect it's due to the canvas.ActualWidth being zero.
Remove this line, and it will work:
content.ColumnDefinitions.Add(new ColumnDefinition { Width = GridLength.Auto });
Note: The canvas is never resizing itself to fit it's content. The reason why the Text is visible in your example, is that the canvas is not clipping it's content.
when you set
myCanvas.ClipToBounds = true;
the text will also disappear.